自定义 Gizmo 进阶

上一篇 讲了如何自定义一个跟随节点移动并缩放的 Gizmo,这篇我们将实现一个可以编辑的 Gizmo

class CustomGizmo extends Editor.Gizmo {
    init () {
        // 初始化一些参数
    }

    createGizmoCallBacks () {
        // 创建 gizmo 操作回调

        // 申明一些局部变量
        let startRadius;        // 按下鼠标时记录的圆半径
        let pressx, pressy;     // 按下鼠标时记录的鼠标位置
        let updated;            // 记录 gizmo 是否被操作过

        return {
            /**
             * 在 gizmo 上按下鼠标时触发
             * @property x 按下点的 x 坐标
             * @property y 按下点的 y 坐标
             * @property event mousedown dom event
             */
            start: (x, y, event) => {
                startRadius = this.target.radius;
                pressx = x;
                pressy = y;
                updated = false;
            },

            /**
             * 在 gizmo 上按下鼠标移动时触发
             * @property dx 鼠标移动的 x 位移
             * @property dy 鼠标移动的 y 位移
             * @property event mousedown dom event
             */
            update: (dx, dy, event) => {
                if (dx === 0 && dy === 0) {
                    return;
                }
                updated = true;

                // 获取 gizmo 依附的节点
                let node = this.node;

                // 记录节点信息的 undo 信息,注意参数为节点的 uuid
                _Scene.Undo.recordNode( node.uuid );

                // 获取 svg view 坐标系下点
                let x = pressx + dx, y = pressy + dy;
                // 获取节点世界坐标系下点
                let pos = this._view.pixelToWorld( cc.v2(x, y) );
                // 转换坐标点到节点下
                pos = node.convertToNodeSpaceAR(pos);
                // 计算 radius
                let radius = pos.mag();
                // js 在做一些计算后会出现小数位过长的情况, Editor.Math.toPrecision 会帮助做一些小数位的截取
                let minDifference = Editor.Math.numOfDecimalsF(1.0/this._view.scale);
                this.target.radius = Editor.Math.toPrecision(radius, minDifference);

                // 更新 gizmo view 
                this._view.repaintHost();
            },

            /**
             * 在 gizmo 抬起鼠标时触发
             * @property event mousedown dom event
             */
            end: (event) => {
                // 判断是否有操作过 gizmo, 没有则跳过处理
                if (updated) {
                    // 如果 gizmo 有修改需要进入 animation 编辑的属性,需要调用此接口来更新数据
                    // _Scene.AnimUtils.recordNodeChanged(this.node);

                    // 推送修改到 undo 下,结束 undo
                    _Scene.Undo.commit();
                }
            }
        };
    }

    onCreateRoot () {
        // 创建 svg 根节点的回调,可以在这里创建你的 svg 工具
        // this._root 可以获取到 Editor.Gizmo 创建的 svg 根节点

        // 实例:

        // 创建一个 svg 工具
        // group 函数文档 : http://documentup.com/wout/svg.js#groups
        this._tool = this._root.group();

        let circle = this._tool.circle()
            // 设置 circle fill 样式
            .fill( { color: 'rgba(0,128,255,0.2)' } )
            // 设置 circle stroke 样式
            .stroke( { color: 'rgba(0,128,255,0.4)', width: 1 } )
            // 设置 circle 的点击区域,这里设置的是根据 fill 模式点击
            .style( 'pointer-events', 'fill' )
            // 设置 circle 鼠标样式
            .style( 'cursor', 'pointer' )
            ;

        // 为 tool 定义一个绘画函数,可以为其他名字
        this._tool.plot =  (radius, position) => {
            this._tool.move(position.x, position.y);
            circle.radius(radius);
        };

        // 创建 gizmo 操作回调函数
        let callbacks = this.createGizmoCallBacks();

        // 为 tool 添加一个操作回调
        // 当在 tool 上按下鼠标时,会创建一个 drag mask
        // 如果不需要此辅助函数,可以自行对 tool 注册 mousedown, mousemove, mouseup 来进行操作
        Editor.GizmosUtils.addMoveHandles( this._tool, {cursor: 'pointer'}, callbacks );
    }

    onUpdate () {
        // 更新 svg 工具

        // 获取 gizmo 依附的组件
        let target = this.target;

        // 获取 gizmo 依附的节点
        let node = this.node;

        // 获取组件半径
        let radius = target.radius;

        // 获取节点世界坐标
        let worldPosition = node.convertToWorldSpaceAR(cc.p(0, 0));

        // 转换世界坐标到 svg view 上
        // svg view 的坐标体系和节点坐标体系不太一样,这里使用内置函数来转换坐标
        let viewPosition = this._view.worldToPixel(worldPosition);

        // 对齐坐标,防止 svg 因为精度问题产生抖动
        let p = Editor.GizmosUtils.snapPixelWihVec2( viewPosition );

        // 获取世界坐标下圆半径
        let worldPosition2 = node.convertToWorldSpaceAR(cc.p(radius, 0));
        let worldRadius = worldPosition.sub(worldPosition2).mag();
        worldRadius = Editor.GizmosUtils.snapPixel(worldRadius);

        // 移动 svg 工具到坐标
        this._tool.plot(worldRadius, p);
    }
}

module.exports = CustomGizmo;