精华内容
下载资源
问答
  • cocos2d-x 地图双指缩放,单指拖动,双击自动放大
  • 通过cocos2d-x实现:TiledMap地图的学习-双指缩放,单指拖动,双击自动放大。
  • 根据http://www.raywenderlich.com/1163/how-to-make-a-tile-based-game-with-cocos2d 中...二、Touch事件的检测,拖动缩放操作 一、实现Tiled免费地图工具编辑 1、安装工具,从Tiled Map Editor下载并且安装

    根据http://www.raywenderlich.com/1163/how-to-make-a-tile-based-game-with-cocos2d 中的介绍来在cocos2d-x上实现。


    文章内容:

    一、Tiled工具的使用

    二、Touch事件的检测,拖动和缩放操作


    一、实现Tiled免费地图工具编辑

    1、安装工具,从Tiled Map Editor下载并且安装,然后打开

    按左上角那个new一个新地图:


    地图大小:块的数量,并不是像素

    块大小:就是1个方块的大小,地图由N个方块组成


    然后创建地图资源,点击工具栏:地图-新图块



    名称自定义,当然符合一般命名标准,- -切忌中文。

    图像-浏览-选择下载的素材


    其他定义根据刚刚新建地图的设置来,注意边距跟制作的图像有关系,上图我们看到每个小块都有1个1像素的透明黑边,其实这个就是为了块图之间能保证其正常像素不偏差,和unity3d中生成atlas是一个道理的,也可以设置padding调整,扯远了。哈哈

    大家摸索下这些按钮就应该懂什么意思了。我制作好的图。

    我设置了我的layer为Background





    2、把TMX文件提供给cocos2d-x项目并且使用上去


    右键-add files这步就不说了。

    代码部分:

    在HelloWorld的layer头文件增加2个关于tmx对象的定义,一个地图,一个地图层



    Helloworld实现文件的操作



    然后大胆地点击编译吧!


    惊恐一启动就暂停了。

    淡定,一看就知道是创建tmx map时候报错了。

    我们观察下导入的TMX文件内容:


    选中的部分就是需要改的,这里注意了:

    因为我的地图文件png是放到下载下面的,然而编译时候,是不会存在../Downloads……这个目录。

    我们改成直接就是Resource根目录:



    再运行:



    很帅是么? 这里我们注意看,地图显示的就是左下角,也就是制作的地图的(0,0)点在左下角对着屏幕左下角。下面将尝试地图的移动以及缩放等操作!




    二、Touch事件的检测

    virtual void registerWithTouchDispatcher();
    virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent); //这个方法必须实现
    virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);


    首先在Helloworld的头文件中虚继承关于CCTouchDispatcher、
    TouchDelegate的一些事件。下面我们认识下这3个方法:

    关于virtual void registerWithTouchDispatcher(); 首先这个用来干嘛的呢?

    /** @brief CCTouchDispatcher.
     Singleton that handles all the touch events.
     The dispatcher dispatches events to the registered TouchHandlers.
     There are 2 different type of touch handlers:
       - Standard Touch Handlers
       - Targeted Touch Handlers
     
     The Standard Touch Handlers work like the CocoaTouch touch handler: a set of touches is passed to the delegate.
     On the other hand, the Targeted Touch Handlers only receive 1 touch at the time, and they can "swallow" touches (avoid the propagation of the event).
     
     Firstly, the dispatcher sends the received touches to the targeted touches.
     These touches can be swallowed by the Targeted Touch Handlers. If there are still remaining touches, then the remaining touches will be sent
     to the Standard Touch Handlers.
    
     @since v0.8.0
     */

    上面是官方定义的CCTouchDispatcher的描述,我们知道有2种实现和处理触碰的消息,标准触碰、目标触碰。

    http://cn.cocos2d-x.org/document/index/class?url=da/de4/classcocos2d_1_1_c_c_touch_dispatcher.html 这是官方中文翻译的一个解释。

    标准触碰是统一处理所有的消息,而目标触碰就是单一的处理,并且能过滤其他消息。所以一般我们准确性触碰的处理建议还是使用目标触碰模式。

    怎么启动Targeted Touch Handlers??


    如果了解监听者模式的应该瞬间能理解,默认在初始化时候

    1、设置

    this->setTouchEnabled(true);

    2、继承和实现

    定义

    virtual void registerWithTouchDispatcher();
    实现如下:
    void HelloWorld::registerWithTouchDispatcher(){
        CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, true);
    }

    这里说一下,正常的监听者模式需要:使用前注册,使用后移除!当我们继承和实现了

    registerWithTouchDispatcher

    那么移除的操作会自动在OnExit时候执行,不需要我们去关心!


    3、使用想要监听的触碰消息方法,注意一个触碰一定会有“开始”,所以

    virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent); //这个方法必须实现

    其他方法可以从CCTouchDispatcher定义里面查找。


    三、iOS多点触碰的使用,实现双指缩放,单指拖动,双击自动缩放和移到目标位置!

    注意:以下先说明怎么启动“标准触碰”方式,但这个方式我不建议,因为~~~后面会看到原因!

    1、启动iOS端多点触碰,默认是关闭的

    //open iOS multiple touch.
        [__glView setMultipleTouchEnabled:YES];

    把以上添加到ios目录中的AppController.mm程序启动回调里面。也就是默认iOS启动处理文件。

    2、Layer检测触碰信息方法

    2.1、需要启动

    layer初始化时候启动触碰支持: this->setTouchEnabled(true);

    2.2、继承实现触碰的方法

    // optional
         virtual void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouches); CC_UNUSED_PARAM(pEvent);}
         virtual void ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouches); CC_UNUSED_PARAM(pEvent);}
         virtual void ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouches); CC_UNUSED_PARAM(pEvent);}
         virtual void ccTouchesCancelled(CCSet *pTouches, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouches); CC_UNUSED_PARAM(pEvent);}

    以上方法就是在CCTouchDelegateProtocol里面,注意观察和上述第一点说的目标触碰不一样就是,这里处理的是所有信息,从传参可看,pTouches代表了多点的意思。

    通常大家从cocos2d-iPhone的例子中可以用

        UITouch* touch1 = [[[event allTouches] allObjects] objectAtIndex:0];
        UITouch* touch2 = [[[event allTouches] allObjects] objectAtIndex:1];
    没错,感觉CCSet是不是和NSSet很像呢?但我很肯定告诉你,这里是不一样的,CCSet并不拥有objectAtIndex这个方法,当然你也可以用它的迭代器去取每一个触碰点。

    但我实践可以告诉你,结果并不如意。即使你固定这个CCTouch* touch1->GetID() == 0 来判断,用“标准触碰”来检测双指缩放,依旧会出现参杂的情况!参杂也就是明明2只手指,但却突然会检测到是1只手指的时候!!kiding me?抓狂

    经过参看CCScrollView中,我发现,原来注册使用“目标触碰”来处理多点触碰其实非常好!因为我前面说了,每一个触碰点都会单独经历Begin->Moved->End;所以我们可以新建一个CCArray来记录进入Begin的触碰CCTouch点!

    bool SimpleDragZoomLayer::ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent){
        if (m_pTouches->containsObject(pTouch) == false) {
            m_pTouches->addObject(pTouch);
        } ……

    然后分别通过m_pTouches的数量来检测是单指操作,还是双指操作!

    下面是我的一个实现逻辑,当然你有更好的解决,或者针对我的实现可以帮忙提高下代码的控制用户体验非常欢迎提供反馈~


    具体内容请看实现的例子项目,使用的话,可以直接用

        //create zoom and drag controller for this layer.
        SimpleDragZoomLayer::create(_tiledMap);

    把它赋给需要控制的layer即可,当然一般是地图层!

    项目源代码:分数有点多~实在抱歉了。尊重版权~谢谢~微笑

    http://download.csdn.net/detail/chiuan/5060318



    展开全文
  • 上一章中,我们使用TiledMap制作了一张简单的地图,并把它加入到了程序中,紧接着本章将实现地图的双指缩放和单指移动功能。 是不是觉得这一功能很接地气,呵呵,其实在很多类似的...双指缩放,单指拖动的实现

    我们使用TiledMap制作了一张简单的地图,并把它加入到了程序中,紧接着本章将实现地图的双指缩放和单指移动功能。

    是不是觉得这一功能很接地气,呵呵,其实在很多类似的大地图背景游戏中这是非常常见和必要的一项功能,玩家可以通过滑动屏幕实现地图的滚动预览,同时可以通过两个手指的拉伸和聚拢实现地图背景的放大缩小。下面,就跟着我们一起来实现吧。

    p1

    双指缩放,单指拖动的实现

    Cocos2d-x中有自己的一套事件分发机制,如果你还不是很清楚,可先阅读Cocos2d-x事件分发机制一文。

    在Cocos2d-x 3.x 中,实现触摸响应的一般流程如下:

    1. 重载触摸回调函数
    2. 创建并绑定触摸事件
    3. 实现触摸回调函数

    具体实现如下:

    1、首先,在GameScene.h文件中声明成员函数。

    1
    2
    virtual void onTouchesBegan(const std::vector<cocos2d::Touch*>& touches, cocos2d::Event *event);
    virtual void onTouchesMoved(const std::vector<cocos2d::Touch*>& touches, cocos2d::Event *event);

    2、在GameScene.cpp文件的init函数中创建并绑定触摸事件。

    1
    2
    3
    4
    5
    6
    7
    // 1 创建一个事件监听器
    auto listener = EventListenerTouchAllAtOnce::create();
    // 2 绑定触摸事件
    listener->onTouchesBegan = CC_CALLBACK_2(GameScene::onTouchesBegan, this);// 触摸开始时触发
    listener->onTouchesMoved = CC_CALLBACK_2(GameScene::onTouchesMoved, this);// 触摸移动时触发
    // 3 添加监听器
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, bgSprite);
    1. 在使用触摸事件时,我们首先需要创建一个事件监听器,事件监听器包含了触摸事件、键盘响应事件、加速记录事件、鼠标响应事件和自定义事件。其中的触摸监听类型触摸事件又分为 EventListenerTouchOneByOne(单点触摸) 和 EventListenerTouchAllAtOnce(多点触摸) 两种。
    2. 让监听器绑定事件处理函数。上面绑定的onTouchesBegan和onTouchesMoved分别响应的是触摸点击开始事件和移动事件。与之相关的还有onTouchEnded和onTouchCancelled两个事件处理函数,但目前我们的游戏还不需要(也有可能不会用到),所以这里就不用实现了。
    3. 监听器创建完成后需要把它绑定给_eventDispatcher事件分发器,_eventDispatcher 是 Node 的属性,通过它我们可以统一管理当前节点(如:场景、层、精灵等)的所有事件分发情况。 
      将事件监听器 listener 添加到事件调度器_eventDispatcher中有两种方法,即如下的两个函数:
      1
      2
      void addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node);             
      void addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority);

      两者的主要区别在于它们加入到事件分发器中的优先级的差异。其中的使用 addEventListenerWithSceneGraphPriority 方法添加的事件监听器优先级固定为0;而使用 addEventListenerWithFixedPriority 方法添加的事件监听器的优先级则可以自己设置,但不可以设置为 0,因为这个是保留给 SceneGraphPriority使用的。

    3、最后在GameScene.cpp文件中实现触摸回调函数 
    一旦玩家开始触碰屏幕,我们的程序就会开始调用相应的触摸事件处理函数来处理相应的逻辑,所以现在我们就可以来完成这部分的逻辑了。

    实现中有以下几个需要注意的问题:

    • 需要判断触碰是单点还是多点,如果是多点,那么就缩放;是单点,就拖动。
    • 节点缩放的参考点默认是其锚点。显然,对于一个大地图背景来说,如果不实时改变它的锚点位置和本身位置,那它的缩放必然不会按照选取的区域进行缩放,必然会出现类似下图的情况。

    p2

    (上图中,背景图片的锚点在蓝点的位置,当我们想放大红圈所圈的那棵树时,如果只是简单的改变背景的放大倍率,那一定会出现上图的第二种情况(目标会向右上角偏移);但如果我们把背景的锚点和位置都设置到目标处,那就会像第三中情况一样,得到一个比较好的放大效果。)

    • 当缩放到一定程度,如缩小到与可视区域一样时,为了避免出现空白的区域,我们需要做一些处理。同时地图不能无止境的放大或缩小,需要有一定的范围来约束。比如,放大到它本身的4倍时,应该停止放大。
    • 拖动地图移动时,地图不能移出可视区域,这里需要做边界控制。

    掌握了这些注意事项以后,现在我们就可以开始具体的行动了。

    首先,在GameScene.h中定义如下的变量:

    1
    2
    Sprite* bgSprite;
    Vec2 bgOrigin;

    bgSprite是地图背景,需要缩放和移动的对象都是其子节点,这样我们就可以通过操作它来实现缩放和移动了。bgOrigin用于记录bgSprite的初始原点位置。

    接着,我们跳转到GameScene.cpp的init()方法,修改之前添加地图背景的方法,同时初始化bgOrigin。如下代码所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    mapLayer = Layer::create();
    this->addChild(mapLayer,-1);
     
    bgSprite = Sprite::create("2.jpg");
    bgSprite->setAnchorPoint(Vec2::ZERO);
    bgSprite->setPosition(Vec2::ZERO),
    bgOrigin = Vec2(Vec2::ZERO);
    mapLayer->addChild(bgSprite);
     
    auto treeSprite = Sprite::create("1.png");
    treeSprite->setAnchorPoint(Vec2::ZERO);
    treeSprite->setPosition(Vec2::ZERO),
    treeSprite->setScale(2);
    bgSprite->addChild(treeSprite, 2);
     
    auto map = TMXTiledMap::create("mymap4.tmx");
    map->setAnchorPoint(Vec2::ZERO);
    map->setPosition(Vec2::ZERO),
    bgSprite->addChild(map, 1);

    因为对层而言,它相比于其他的节点来说,其锚点、位置、大小都不好控制,所以我们需要通过另外的节点(比如这里的bgSprite)来执行后面的缩放和移动等动作。

    最后压轴来了,实现触摸事件的处理函数如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    void GameScene::onTouchesMoved(const std::vector<Touch*>& touches, Event  *event)
    {
        auto winSize = Director::getInstance()->getWinSize();
        if(touches.size() > 1)        // 多点进行缩放
        {
            // 得到当前两触摸点
            auto point1 = touches[0]->getLocation();
            auto point2 = touches[1]->getLocation();
            // 计算两点之间得距离
            auto currDistance = point1.distance(point2);
            // 计算两触摸点上一时刻之间得距离
            auto prevDistance = touches[0]->getPreviousLocation().distance(touches[1]->getPreviousLocation());
            // 两触摸点与原点的差向量,pointVec1和pointVec2是相对于bgSprite的位置
            auto pointVec1 = point1  - bgOrigin;
            auto pointVec2 = point2  - bgOrigin;
            // 两触摸点的相对中点
            auto relMidx = (pointVec1.x + pointVec2.x) / 2 ;
            auto relMidy = (pointVec1.y + pointVec2.y) / 2 ;
            // 计算bgSprite的锚点
            auto anchorX = relMidx / bgSprite->getBoundingBox().size.width;
            auto anchorY = relMidy / bgSprite->getBoundingBox().size.height;
            // 相对屏幕的中点
            auto absMidx = (point2.x + point1.x) / 2 ;
            auto absMidy = (point2.y + point1.y) / 2 ;
     
            // 缩放时,为了避免出现空白的区域,需要做以下的边界处理。       
            // 当bgSprite快要进入到屏幕时,修改bgSprite的位置(既absMidx和absMidy)。
            if(  bgOrigin.x > 0)
            {
                absMidx -= bgOrigin.x;
            }
            if( bgOrigin.x < -bgSprite->getBoundingBox().size.width + winSize.width )
            {
                absMidx +=  -bgSprite->getBoundingBox().size.width + winSize.width - bgOrigin.x;
            }
            if( bgOrigin.y > 0 )
            {
                absMidy -= bgOrigin.y;
            }
            if( bgOrigin.y < -bgSprite->getBoundingBox().size.height + winSize.height )
            {
                absMidy +=  -bgSprite->getBoundingBox().size.height + winSize.height - bgOrigin.y;
            }
            // 重设bgSprite锚点和位置
            bgSprite->setAnchorPoint(Vec2(anchorX, anchorY));
            bgSprite->setPosition(Vec2(absMidx, absMidy));
            // 根据两触摸点前后的距离计算缩放倍率
            auto scale = bgSprite->getScale() * ( currDistance / prevDistance);
            // 控制缩放倍率在1~4倍之间,最小倍率不能太小,不让背景将不能填充满整个屏幕。
            scale = MIN(4,MAX(1, scale));
            bgSprite->setScale(scale);
            // 更新原点位置
            bgOrigin = Vec2(absMidx, absMidy) - Vec2(bgSprite->getBoundingBox().size.width * anchorX, bgSprite->getBoundingBox().size.height * anchorY) ;
        }
        else if(touches.size() == 1)        // 单点进行移动
        {
            // 单点时,touches中只有一个Touch对象,所以通过touches[0]就可以得到触摸对象
            auto touch = touches[0];
            // 计算滑动过程中的滑动增量
            auto diff = touch->getDelta();      
            // 得到当前bgSprite的位置
            auto currentPos = bgSprite->getPosition();
            // 得到滑动后bgSprite应该所在的位置
            auto pos = currentPos + diff;
            // 得到此刻bgSprite的尺寸
            auto bgSpriteCurrSize = bgSprite->getBoundingBox().size;
     
            //边界控制,约束pos的位置
            pos.x = MIN(pos.x, bgSpriteCurrSize.width * bgSprite->getAnchorPoint().x);
            pos.x = MAX(pos.x, -bgSpriteCurrSize.width + winSize.width + bgSpriteCurrSize.width * bgSprite->getAnchorPoint().x);
            pos.y = MIN(pos.y, bgSpriteCurrSize.height * bgSprite->getAnchorPoint().y);
            pos.y = MAX(pos.y, -bgSpriteCurrSize.height + winSize.height + bgSpriteCurrSize.height * bgSprite->getAnchorPoint().y);
            // 重设bgSprite位置
            bgSprite->setPosition(pos);
     
            // 更新原点位置
            if( pos.x >= bgSpriteCurrSize.width * bgSprite->getAnchorPoint().x
               || pos.x <= -bgSpriteCurrSize.width + winSize.width + bgSpriteCurrSize.width * bgSprite->getAnchorPoint().x)
            {
                diff.x = 0;
            }
            if( pos.y >= bgSpriteCurrSize.height * bgSprite->getAnchorPoint().y
               || pos.y <= -bgSpriteCurrSize.height + winSize.height + bgSpriteCurrSize.height * bgSprite->getAnchorPoint().y)
            {
                diff.y = 0;
            }
            bgOrigin += diff;
        }
    }

    以上就是onTouchesMoved函数的实现方法了,原理已在注释中解释清楚,所以我想理解起来已经不会很难。下面给出一张示意图帮助大家理解:

    p3

    下图是缩放过程中刚好出现空白的区域时的图形示意图:

    p4

    此时空白的区域的宽等于 -bgSprite->getBoundingBox().size.width + winSize.width - bgOrigin.x,所以我们把背景的位置向右移动-bgSprite->getBoundingBox().size.width + winSize.width - bgOrigin.x个单位就可以避免这种情况的出现。

    代码中有一点需要注意的是,在缩放过程中,bgSprite的尺寸不断变化的,所以计算起锚点或进行边界处理时,一定要用它缩放后的尺寸宽高来计算,而不能是它本身的宽高。 所以代码中计算bgSprite的尺寸我们用getBoundingBox函数来获得经过缩放和旋转之后的外框盒大小,而不用getContentSize函数来获得节点原始的大小。

    iOS端多点触碰默认是关闭的,所以需要在AppController.mm 程序启动回调中启用多点触摸才可以,具体方法是在以下的函数段后加入[eaglView setMultipleTouchEnabled:YES];

    如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    CCEAGLView *eaglView = [CCEAGLView viewWithFrame: [window bounds]
                                     pixelFormat: kEAGLColorFormatRGBA8
                                     depthFormat: GL_DEPTH24_STENCIL8_OES
                              preserveBackbuffer: NO
                                      sharegroup: nil
                                   multiSampling: NO
                                 numberOfSamples: 0];
     
    [eaglView  setMultipleTouchEnabled:YES];

    总的来说,要想很好的实现这一功能不是容易的,以上就是我们实现了的一种方法,虽然细节上还有一些问题,也未在真机上测试,但还是希望能对大家的学习有所帮助。如果你有更好的方法实现,也可以提出来,大家一起进步学习。

    展开全文
  • 吐血写了这个地图拖动及缩放单指拖动 双指缩放 防出界 self._map_node --地图 self._map_size = {x, y} --地图大小 self._map_scale --地图缩放 self._map_min_scale --地图最小缩放 self._map_max...

    吐血写了这个地图拖动及缩放 
    单指拖动 双指缩放 防出界

     

    self._map_node  --大地图
    self._map_size = {x, y} --大地图大小
    self._map_scale --大地图缩放
    self._map_min_scale --大地图最小缩放
    self._map_max_scale --大地图最大缩放

     

    -- 点击监听
    local listener = cc.EventListenerTouchOneByOne:create()
    listener:setSwallowTouches(false)
    -- 点击列表
    local touchPoint = {}
    -- 第一个点击初始位置
    local pos_s
    local function onTouchBegan(touch, event)
        -- 最多处理两指
        table.insert(touchPoint, {id=touch:getId(), pos=touch:getLocation()})
        if #touchPoint > 2 then
            table.remove(touchPoint, #touchPoint)
            return false
        end
        pos_s = touch:getLocation()
        return true
    end
    
    local function onTouchMoved(touch, event)
        if #touchPoint == 1 then
            local pos = touch:getLocation()
            -- 移动的位置
            local moveX = pos_s.x-pos.x
            local moveY = pos_s.y-pos.y
            -- 移动到的位置
            local toX = self._map_node:getPositionX()-moveX
            local toY = self._map_node:getPositionY()-moveY
            -- 变化之后的四角坐标
            local posLeft = self._map_size.x/2*self._map_scale-toX --左坐标
            local posBottom = self._map_size.y/2*self._map_scale-toY -- 下坐标
            local posRight = self._map_size.x/2*self._map_scale+g_visibleSize.width-toX -- 右坐标
            local posTop = self._map_size.y/2*self._map_scale+g_visibleSize.height-toY -- 上坐标
            -- 调整位置 防止出框
            if posLeft < 0 then toX = self._map_size.x/2*self._map_scale end --
            if posBottom < 0 then toY = self._map_size.y/2*self._map_scale end --
            if posRight > self._map_size.x*self._map_scale then toX = g_visibleSize.width-self._map_size.x/2*self._map_scale end --
            if posTop > self._map_size.y*self._map_scale then toY = g_visibleSize.height-self._map_size.y/2*self._map_scale end --
            -- 跳到位置
            self._map_node:setPosition(toX, toY)
            pos_s = pos
        else
            -- 两个原始坐标
            local touch1 = touchPoint[1]
            local touch2 = touchPoint[2]
            -- 放入新坐标
            if touch:getId() == touch1.id then
                touchPoint[1] = {id=touch:getId(), pos=touch:getLocation()} 
            else
                touchPoint[2] = {id=touch:getId(), pos=touch:getLocation()}
            end
            -- 两个坐标相对位移
            local posX1 = touchPoint[1].pos.x - touch1.pos.x
            local posY1 = touchPoint[1].pos.y - touch1.pos.y
            local posX2 = touchPoint[2].pos.x - touch2.pos.x
            local posY2 = touchPoint[2].pos.y - touch2.pos.y
            -- 距离变化
            local distance_before = (touch1.pos.x-touch2.pos.x)*(touch1.pos.x-touch2.pos.x)+(touch1.pos.y-touch2.pos.y)*(touch1.pos.y-touch2.pos.y)
            local distance_after = (touchPoint[1].pos.x-touchPoint[2].pos.x)*(touchPoint[1].pos.x-touchPoint[2].pos.x)+(touchPoint[1].pos.y-touchPoint[2].pos.y)*(touchPoint[1].pos.y-touchPoint[2].pos.y)
            local distance_diff = distance_after - distance_before
            -- 原来的锚点
            local anchorPoint_before = self._map_node:getAnchorPoint()
            -- 距离左下角距离
            local dis_left = self._map_size.x*anchorPoint_before.x*self._map_scale-self._map_node:getPositionX()+(touchPoint[1].pos.x+touchPoint[2].pos.x)/2
            local dis_bottom = self._map_size.y*anchorPoint_before.y*self._map_scale-self._map_node:getPositionY()+touch1.pos.y+(touchPoint[1].pos.y+touchPoint[2].pos.y)/2
            -- 以两点中心为新的锚点
            local anchorPoint_after = cc.p(dis_left/self._map_scale/self._map_size.x, dis_bottom/self._map_scale/self._map_size.y)
            self._map_node:setAnchorPoint(anchorPoint_after)
            -- 距离差
            local dis_X = self._map_size.x*(anchorPoint_before.x-anchorPoint_after.x)*self._map_scale
            local dis_Y = self._map_size.y*(anchorPoint_before.y-anchorPoint_after.y)*self._map_scale
            -- 位置纠正
            self._map_node:setPosition(self._map_node:getPositionX()-dis_X, self._map_node:getPositionY()-dis_Y)
            -- 缩放
            self._map_scale = self._map_scale + distance_diff/1000000
            -- 限制
            if self._map_scale < self._map_min_scale then self._map_scale = self._map_min_scale end
            if self._map_scale > self._map_max_scale then self._map_scale = self._map_max_scale end
            self._map_node:setScale(self._map_scale)
            -- 防止出界 移动补位
            -- 左侧空白距离
            local posX_left = self._map_size.x*anchorPoint_after.x*self._map_scale-self._map_node:getPositionX() 
            if posX_left < 0 then
                self._map_node:setPositionX(self._map_size.x*anchorPoint_after.x*self._map_scale)
            end
            -- 下侧空白距离
            local posX_bottom = self._map_size.y*anchorPoint_after.y*self._map_scale-self._map_node:getPositionY() 
            if posX_bottom < 0 then
                self._map_node:setPositionY(self._map_size.y*anchorPoint_after.y*self._map_scale)
            end
            -- 右侧空白距离
            local posX_right = self._map_size.x*(1-anchorPoint_after.x)*self._map_scale+self._map_node:getPositionX()-g_visibleSize.width 
            if posX_right < 0 then
                self._map_node:setPositionX(g_visibleSize.width-self._map_size.x*(1-anchorPoint_after.x)*self._map_scale)
            end
            -- 上侧空白距离
            local posX_top = self._map_size.y*(1-anchorPoint_after.y)*self._map_scale+self._map_node:getPositionY()-g_visibleSize.height 
            if posX_top < 0 then
                self._map_node:setPositionY(g_visibleSize.height-self._map_size.y*(1-anchorPoint_after.y)*self._map_scale)
            end
        end
    end
    
    local function onTouchEnded(touch, event)
        -- 清除松开的点击
        if touchPoint[1].id == touch:getId() then
            table.remove(touchPoint, 1)
        elseif touchPoint[2].id == touch:getId() then
            table.remove(touchPoint, 2)
        end
        -- 将剩下的点击设置为第一个点击位置
        if #touchPoint == 1 then
            -- 恢复锚点
            local anchorPoint_before = self._map_node:getAnchorPoint()
            local anchorPoint_after = cc.p(0.5, 0.5)
            self._map_node:setAnchorPoint(anchorPoint_after)
            local dis_X = self._map_size.x*(anchorPoint_before.x-anchorPoint_after.x)*self._map_scale
            local dis_Y = self._map_size.y*(anchorPoint_before.y-anchorPoint_after.y)*self._map_scale
            self._map_node:setPosition(self._map_node:getPositionX()-dis_X, self._map_node:getPositionY()-dis_Y)
            pos_s = touchPoint[1].pos
        end
    end
    
    listener:registerScriptHandler(onTouchBegan, cc.Handler.EVENT_TOUCH_BEGAN)
    listener:registerScriptHandler(onTouchMoved, cc.Handler.EVENT_TOUCH_MOVED)
    listener:registerScriptHandler(onTouchEnded, cc.Handler.EVENT_TOUCH_ENDED)    
    self:getEventDispatcher():addEventListenerWithSceneGraphPriority(listener, self._map_node)

     

    介于PC的话不能多点触控 所以特意写了一个键盘监听来模拟多点触控的行为 
    不过对于cc.Touch这个C++类 在存在table中之后会使getId()方法失效 不知道是什么原因 所以干脆就把具体数据存在了表里 
    cc.Touch在使用setTouchInfo()方法的时候 posY的值莫名会变 不知道什么原因 准备在C++中下断点看看- -

    local posX = 300
    local posY = 500
    local pos_move = 1
    
    -- 按键监听
    local listener_key = cc.EventListenerKeyboard:create()
    
    local key_Q = 140
    local key_F = 129
    local ket_E = 128
    local key_W = 146
    local key_S = 142
    local key_A = 124
    local key_D = 127
    local key_R = 141
    local key_T = 143
    
    local function keyToMove(index)
        local touch = cc.Touch:new()
        touch:setTouchInfo(1, posX, posY)
        local event = cc.EventTouch:new()
        event:setEventCode(index)
        if index == 0 then
            onTouchBegan(touch, event)
        elseif index == 1 then
            onTouchMoved(touch, event)
        elseif index == 2 then
            onTouchEnded(touch, event)
        end
    end
    
    local function onKeyReleased(keyCode, event)
        print("键盘操作 keyCode="..keyCode)
        if key_Q == keyCode then
            print("初始化")
            posX = 500
            posY = 500
        elseif key_F == keyCode then
            print("按下另一个鼠标")
            keyToMove(0)
        elseif ket_E == keyCode then
            print("松开另一个鼠标")
            keyToMove(2)
        elseif key_W == keyCode then
            print("向上移动")
            posY = posY - pos_move
            keyToMove(1)
        elseif key_S == keyCode then
            print("向下移动")
            posY = posY + pos_move
            keyToMove(1)
        elseif key_A == keyCode then
            print("向左移动")
            posX = posX - pos_move
            keyToMove(1)
        elseif key_D == keyCode then
            print("向右移动")
            posX = posX + pos_move
            keyToMove(1)
        end
    end
    
    listener_key:registerScriptHandler(onKeyReleased, cc.Handler.EVENT_KEYBOARD_RELEASED )
    self:getEventDispatcher():addEventListenerWithSceneGraphPriority(listener_key, self)

     

    转载于:https://www.cnblogs.com/PandaQ/p/9234745.html

    展开全文
  • 图片拖动&缩放工具类

    2020-04-14 15:34:34
    本工具类(Android)通过传入ImageView来简单实现单指拖动图片和双指放大缩小的功能,代码中几乎对每行代码都有注释。
  • 查看示例效果:一、功能介绍图片预览主要有以下几个功能点组成:监听图片点击事件,进入图片预览模式自定义手势事件, (双指缩放,滑动,双击。。。)监听图片手势事件,通过 transform-matrix 实现图片的各种变换;二...

    查看示例效果:

    一、功能介绍

    图片预览主要有以下几个功能点组成:监听图片点击事件,进入图片预览模式

    自定义手势事件, (双指缩放,滑动,双击。。。)

    监听图片手势事件,通过 transform-matrix 实现图片的各种变换;

    二、实现方法

    1、图片预览模式

    图片预览即点击图片在页面中插入一个黑色全屏背景框并将图片居中显示。封装时,为了只对指定图片添加功能,可通过监听指定类名或添加某种属性的img标签监听;另外需在对背景框绑定点击事件,退出预览模式。一下是一个简单示例代码://点击图片进入预览

    var $Dom = document.querySelector(".preview");

    $Dom.onclick = function() {

    var temp = this.src;

    var objE = document.createElement("div");

    objE.innerHTML = '

    ' +

    ''+temp+'' +

    '

    ';

    document.body.appendChild(objE.children[0]);

    //退出图片预览事件

    var $bg = document.querySelector(".bgM");

    $bg.onclick = function() {

    var dm = document.querySelector(".bgM");

    document.body.removeChild(dm);

    }

    //阻止事件冒泡

    var $img = document.querySelector(".img-custom-img2");

    $img.onclick = function(event) {

    event.stopPropagation();

    }

    }

    复制代码

    css样式参考.bgM{

    width: 100%;

    height: 100%;

    position: absolute;

    top: 0;left: 0;right: 0;bottom: 0;

    z-index: 1000;

    background-color: rgba(0,0,0,0.85);

    overflow: hidden;

    }

    .bgM img{

    width: 100%;

    max-height:100%;

    position: absolute;

    top: 0;left: 0;right: 0;bottom: 0;

    z-index: 1001;

    margin: auto;

    }

    复制代码

    2、自定义手势事件

    这里通过监听移动端touch事件实现自定义双指缩放,单指滑动,双击事件,并通过事件属性传递相关参数,如缩放比例,滑动距离等,详细实现方式参考这篇博客:请参考此博文:https://www.cnblogs.com/pangys/p/9119845.html 这里只大概说明;当触发touch事件的时候,会生成一个TouchEvent对象,我们可通过其属性e.touches.length来判断是否多点触控,通过e.touches[index].pageX,e.touches[index].pageY获取去触点坐标,通过e.target获取dom节点;

    这里为了方便,直接监听document事件然后对目标元素触发事件,实际也可以直接对img监听事件,然后分别处理;

    (1)手势事件监听touchstart事件,若e.touches.length>=2,为双指事件,获取触点坐标(触点坐标-目标元素.offsetLeft/Top)计算两个点中点 添加到事件属性中,改变相关状态,触发gesturestart事件;

    监听touchmove事件,若e.touches.length>=2,获当前取触点坐标和gesturestart坐标,计算出缩放比例及角度,触发gesturechange事件;

    监听touchend事件,根据前面事件记录的状态触发结束gestureend事件;

    (2)滑动事件监听touchstart事件,若e.touches.length<2,为单指事件,获取触点坐标(触点坐标-目标元素.offsetLeft/Top)添加到事件属性中,记录事件状态;

    监听touchmove事件,若e.touches.length<2,获当前取触点坐标和上一步坐标,计算出移动距离添加到事件属性中,触发swipeMove事件;

    (3)双击事件

    监听touchstart事件,若e.touches.length<2,为单指事件,获取触点坐标(触点坐标-目标元素.offsetLeft/Top)添加到事件属性中,获取当前时间挫记录到相关变量中,计算本次时间戳与上次事件时间戳之差,若时间差范围在指定范围(0~250)则触发doubleTouch事件;

    (4)单击事件

    监听touchstart事件,使用延时器450ms触发单击事件,若在450ms无其他事件则触发单击事件

    参考代码:var isTouch = false;

    var isDoubleTouch = false; //是否为多触点

    var start = []; //存放触点坐标

    var now, delta; //当前时间,两次触发事件时间差

    var timer = null; //计时器,触发单击事件

    var startPosition, movePosition, endPosition; //滑动起点,移动,结束点坐标

    //事件声明

    var gesturestart = new CustomEvent('gesturestart');

    var gesturechange = new CustomEvent('gesturechange');

    var gestureend = new CustomEvent('gestureend');

    var swipeMove = new CustomEvent('swipeMove');

    var doubleTouch = new CustomEvent("doubleTouch");

    var oneTouch = new CustomEvent("oneTouch");

    //监听touchstart事件

    document.addEventListener('touchstart', function(e) {

    if (e.touches.length >= 2) { //判断是否有两个点在屏幕上

    isDoubleTouch = true;

    start = e.touches; //得到第一组两个点

    var screenMinPoint = getMidpoint(start[0], start[1]); //获取两个触点中心坐标

    gesturestart.midPoint = [screenMinPoint[0] - e.target.offsetLeft, screenMinPoint[1] - e.target.offsetTop]; //获取中心点坐标相对目标元素坐标

    e.target.dispatchEvent(gesturestart);

    } else {

    delta = Date.now() - now; //计算两次点击时间差

    now = Date.now();

    startPosition = [e.touches[0].pageX, e.touches[0].pageY];

    if (delta > 0 && delta <= 250) { //双击事件

    clearTimeout(timer);

    doubleTouch.position = [e.touches[0].pageX - e.target.offsetLeft, e.touches[0].pageY - e.target.offsetTop];

    e.target.dispatchEvent(doubleTouch);

    } else { //滑动事件

    timer = setTimeout(function(){

    e.target.dispatchEvent(oneTouch);//单击事件

    },450)

    }

    isTouch = true;

    }

    }, false);

    //监听touchmove事件

    document.addEventListener('touchmove', function(e) {

    clearTimeout(timer);

    if (e.touches.length >= 2 && isDoubleTouch) { //手势事件

    var now = e.touches; //得到第二组两个点

    var scale = getDistance(now[0], now[1]) / getDistance(start[0], start[1]); //得到缩放比例

    var rotation = getAngle(now[0], now[1]) - getAngle(start[0], start[1]); //得到旋转角度差

    gesturechange.scale = scale.toFixed(2);

    gesturechange.rotation = rotation.toFixed(2);

    e.target.dispatchEvent(gesturechange);

    } else if (isTouch) {

    movePosition = [e.touches[0].pageX, e.touches[0].pageY];

    endPosition = movePosition;

    movePosition = [movePosition[0] - startPosition[0], movePosition[1] - startPosition[1]];

    startPosition = [e.touches[0].pageX, e.touches[0].pageY];

    swipeMove.distance =[movePosition[0].toFixed(2) , movePosition[1].toFixed(2)];

    e.target.dispatchEvent(swipeMove);

    }

    }, false);

    //监听touchend事件

    document.addEventListener('touchend', function(e) {

    if (isDoubleTouch) {

    isDoubleTouch = false;

    gestureend.position = endPosition;

    e.target.dispatchEvent(gestureend);

    };

    }, false);

    /*

    * 两点的距离

    */

    function getDistance(p1, p2) {

    var x = p2.pageX - p1.pageX,

    y = p2.pageY - p1.pageY;

    return Math.sqrt((x * x) + (y * y));

    };

    /*

    * 两点的夹角

    */

    function getAngle(p1, p2) {

    var x = p1.pageX - p2.pageX,

    y = p1.pageY - p2.pageY;

    return Math.atan2(y, x) * 180 / Math.PI;

    };

    /*

    * 获取中点

    */

    function getMidpoint(p1, p2) {

    var x = (p1.pageX + p2.pageX) / 2,

    y = (p1.pageY + p2.pageY) / 2;

    return [x, y];

    }

    复制代码

    三、图片的变换

    对于图片的每次操作都需在上一次操作的基础上进行叠加,如果直接使用width,top,left或scale,translate等css样式需要每次都记录当前图片状态的全部参数,而且计算较多,这里考虑使用transform-matrix实现图片的基本变换,这样只需创建一个数组作为变换矩阵,每次操作直接在当前变换矩阵上修改相关参数即可实现图像的变换:transform-matrix :可配置[a,b,c,d,e,f]6个参数,如下图所示,x和y是初始的坐标,x’ 和y’则是通过矩阵变换后得到新的坐标。变换矩阵,对原先的坐标施加变换,就能得到新的坐标了。依据矩阵变换规则即可得到: x’=ax+cy+e y’=bx+dy+f。

    1d260a2f2b76486ee8240de1074a8a76.gif变换x方向y方向缩放ad

    移动ef

    (1) 获取目标元素及相关参数,绑定事件var $imgs = document.querySelector("#img_scan");

    var clientWidth = document.body.clientWidth; //窗口宽

    var clientHeight = document.body.clientHeight; //窗口高

    var imgWidth = parseInt(window.getComputedStyle($imgs).width); //图片宽

    var imgHeight = parseInt(window.getComputedStyle($imgs).height); //图片高

    $imgs.addEventListener('gesturestart', gesturef, false);

    $imgs.addEventListener('gesturechange', gesturef, false);

    $imgs.addEventListener('gestureend', gesturef, false);

    $imgs.addEventListener('swipeMove', gesturef, false);

    $imgs.addEventListener('doubleTouch', gesturef, false);

    $imgs.addEventListener('oneTouch', gesturef, false);

    var tMatrix = [1, 0, 0, 1, 0, 0]; //x缩放,无,无,y缩放,x平移,y平移

    var originLast, maxSwipeLeft, maxSwipeRight, maxSwipeTop, maxSwipeBottom; //上下左右可拖动距离

    复制代码

    (2)监听 gesturestart 设置 变换中心case "gesturestart":

    var x = event.midPoint[0];

    var y = event.midPoint[1];

    originLast = event.midPoint;

    $imgs.style.transformOrigin = x + "px " + y + "px";

    break;

    复制代码

    (2)监听 gesturechange 进行缩放变换,这里设置了缩放范围为0.5 ~ 3;case "gesturechange":

    var sc = parseFloat(event.scale);

    tMatrix[0] = tMatrix[0] + sc - 1 > 0.5 && tMatrix[0] + sc - 1 < 3 ? tMatrix[0] + sc - 1 : tMatrix[0];

    tMatrix[3] = tMatrix[3] + sc - 1 > 0.5 && tMatrix[3] + sc - 1 < 3 ? tMatrix[3] + sc - 1 : tMatrix[3];

    var temp = tMatrix.join(",");

    $imgs.style.transform = "matrix(" + temp + ")";

    break;

    复制代码

    (3)监听 gestureend 获取移动边界范围边界case "gestureend":

    maxMove();

    break;

    复制代码

    可移动边界范围的计算:

    对于图片中的任意点可拖动范围都是相同的,那么以缩放中心点来计算,如下图所示,对于图片中的缩放中心点p,有缩放后距离边距的距离,可移动的范围均为 缩放后增加或减少的距离 - (缩放中心点距离图片边缘的距离),即 | 缩放比例 - 1 | * p点距离边缘的距离;

    4e9d8b48f010d33b05ae5e01d74f01d2.png

    代码如下:function maxMove(){

    //最大可拖动范围

    var sca = tMatrix[0];

    maxSwipeLeft = Math.abs(sca - 1) * originLast[0];

    maxSwipeRight = Math.abs(sca - 1) * (imgWidth - originLast[0]);

    maxSwipeTop = Math.abs(sca - 1) * originLast[1];

    maxSwipeBottom = Math.abs(sca - 1) * (imgHeight - originLast[1]);

    }

    复制代码

    (4)监听 swipeMove 拖动图片,需考虑是否在可拖动范围if (!maxSwipeLeft || !maxSwipeRight || !maxSwipeTop || !maxSwipeBottom) return;

    if (event.distance[0] > 0 && maxSwipeLeft < tMatrix[4]) return;

    if (event.distance[0] < 0 && maxSwipeRight < -tMatrix[4]) return;

    if (event.distance[1] > 0 && maxSwipeTop < tMatrix[5]) return;

    if (event.distance[1] < 0 && maxSwipeBottom < -tMatrix[5]) return;

    tMatrix[4] = tMatrix[4] + parseInt(event.distance[0]);

    tMatrix[5] = tMatrix[5] + parseInt(event.distance[1]);

    var temp = tMatrix.join(",");

    $imgs.style.transform = "matrix(" + temp + ")";

    break;

    复制代码

    (5)监听 doubleTouch 实现双击点缩放case "doubleTouch":

    originLast = event.position;

    $imgs.style.transformOrigin = event.position[0] + "px " + event.position[1] + "px";

    tMatrix[0] = 2;

    tMatrix[3] = 2;

    var temp = tMatrix.join(",");

    $imgs.style.transform = "matrix(" + temp + ")";

    maxMove();

    break;复制代码

    (6)监听 oneTouch 实现退出预览

    case "oneTouch":

    var $bg = document.querySelector(".bgM");

    document.body.removeChild($bg);

    break;

    复制代码

    展开全文
  • 代码地址http://pangyongsheng.github.io/imgPreview/查看示例效果:一、功能介绍 图片预览主要有以下几个功能点组成:监听图片点击事件,进入图片预览模式自定义手势事件, (双指缩放,滑动,双击。。。)监听图片...
  • 想必图片的拖动和放大缩小是每个人都使用过的功能,最常见于手机相册里,你可以通过一个手指拖动你的照片,同样也可以使用双指通过缩放手势对图片进行相应的缩放。那么这是如何实现的呢? 先来看效果: 实现原理...
  • 1.Https是如何实现的 htttps是基于ssl(Secure Sockets Layer安全套接层)的http协议,https协议在http... ... 2.Android事件流程和OnTouchListener的关系 3.双指缩放拖动大图 4.客户端网络安全实现 1.使用https 2.设...
  • 使用 photoview 双击或双指缩放的ImageView  使用多点触控和双击。 滚动,以平滑滚动甩。 使用起来很简单,可本地加载,网络加载等,可以放大拖动缩小等功能,并且结合viewpager可实现浏览图片并且对图片...
  • 缩小至view大小,否则按原始大小显示双击放大,第一次双击后将图片宽或高放大到view的宽或高的比例再次双击会再在此前基础上放大固定的倍数放大两次后后再次双击可缩小到初始大小双指可对图片大小进行缩放,...
  • gif有点模糊,大概就是这样的功能,进入Dialog中之后,可以单指拖动双指放大,单击退出。 Dialog布局如下: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android...
  • 上面的gif中,依次进行了拖动——>触摸右上角放大,缩小——>触摸上方与右测边缘——>双指放大缩小。 2 使用步骤 2.1 布局。外层一个LinearLayout,里面一个自定义的控件DragScaleView,为了能够更清楚的看到控件的...
  • Omnigraffle快捷键

    2019-09-27 05:54:22
    按住command双指推移 缩放 按住command,旋转物体 按住 option缩放 基于中心缩放 按住t 新建文字 cmd+1234 切换面板 cmd+r 显示标尺 cmd+d 复制一个图形 按住Option拖动Canvas可...
  • 4)触屏缩放手势:在屏幕上能轻松地双指缩放窗口大小及使用各种手势。 5)拖动预览:拖动播放进度条会弹出小窗口预览定点位置的画面。 6)自定义界面:用户自定义流媒体链接界面,添加流媒体播放快捷入口。 7)硬件...
  • 2.支持1-20级的地图随意缩放,鼠标右键拖动漫游。 3.支持名称查询定位,可以输入地点名称后定位到所查询的位置。 4.支持谷歌在线和离线地图,离线时需要地图包;混合卫星地图,普通地图,地形地图,在软件界面上...
  • 布局树布局树可以双指缩放扩展用户可以方便的对SAK进行扩展,只需要继承自AbsCanvas并重新onDraw(Canvas canvas, Paint paint, ViewGroup viewGroup, int startLayer, int endLayer),或者继承自CanvasLayerAdapter并...

空空如也

空空如也

1 2
收藏数 23
精华内容 9
关键字:

双指缩放拖动大图