Cocos Creator 实现画板(你画我猜)

前言

本例是基于cocos官方推荐raphael实现的。其中画板与看板的同步处理我采用的是在画板画完一条线之后看板再同步数据来显示画线过程。

一、实现效果如下:

二、画板核心代码

1、初始化画板参数

 onLoad: function () {

        // 初始化参数
        this.lineWidth = 5;
        this.strokeColor = cc.color(0, 0, 0);
        this.isClearMode = false;
        this.group = this.addComponent('R.group');

        // 绑定触摸通知事件通知
        cc.eventManager.addListener({
            event: cc.EventListener.TOUCH_ONE_BY_ONE,
            onTouchBegan: this.onTouchBegan.bind(this),
            onTouchMoved: this.onTouchMoved.bind(this),
            onTouchEnded: this.onTouchEnded.bind(this),
        }, this.node);
    },

需要注意的事项:

① 本例使用的是 raphael 库的 group 的概念来处理的画板中的线

2、触摸开始事件回调处理

   onTouchBegan: function (touch, event) {

        // 初始一条线的数据
        this.dataDict = {};
        this.dataDict.dataEvent = 'draw';

        // 获取开始时间点
        this.dataDict.startTime = new Date().getTime();

        // 获取触摸点数据
        var touchLoc = touch.getLocation();
        touchLoc = this.node.parent.convertToNodeSpaceAR(touchLoc);

        // 从group获取一条Path实例
        var path = this.group.addPath();
        path.fillColor = 'none';

        // 判断是否是橡皮擦状态
        if (this.isClearMode) {

            path.lineWidth = 15;
            path.strokeColor = cc.color(255, 255, 255);
        } else {

            path.lineWidth = this.lineWidth;
            path.strokeColor = this.strokeColor;
        }

        this.dataDict.strokeColor = path.strokeColor.toHEX("#rrggbb");
        this.dataDict.lineWidth = path.lineWidth;

        // 初始化点数组,并赋值开始位置的点
        this.points = [touchLoc];

        return true;
    },

需要注意的事项:

① 橡皮擦处理的方式是用与画板背景色相同的颜色画线,并加粗线的宽度来实现的。

this.dataDict 记录的数据是为了通过 webSocket 传递到看板,来使看板同步画线。

3、触摸移动事件处理

onTouchMoved: function (touch, event) {

        // 获取触摸点数据
        var touchLoc = touch.getLocation();
        touchLoc = this.node.parent.convertToNodeSpaceAR(touchLoc);

        // 添加到点数组内
        this.points.push(touchLoc);

        // 获取当前画的path实例,并更新内部展现点数据
        var path = this.group.children[this.group.children.length - 1];
        path.points(this.points);
    },

需要注意的事项:

① 每次获取的都是group中最新的一条path去画。

4、触摸事件结束处理

 onTouchEnded: function(touch, event) {

        // 获取触摸点数据
        var path = this.group.children[this.group.children.length - 1];
        path.points(this.points);

        // 获取结束时间点
        this.dataDict.endTime = new Date().getTime();

        // 将点数组转化为相对于node宽高的映射位置
        this.pointDicts = [];
        for (var i = 0; i < this.points.length; i++) {

            let point = this.points[i];
            var pointDict = {};
            pointDict.x = point.x / this.node.width;
            pointDict.y = point.y / this.node.height;
            this.pointDicts.push(pointDict);
        }
        this.dataDict.points = this.pointDicts;

        let sendData = this.dataDict;

        // 本地测试同步数据
        // this.lookDraw.startDraw(sendData);

        // 网络同步数据
        if (window.room_user) {

            var drawAction = gameAction.getDrawDataAction(window.room_user, sendData)
            happySocket.sendData(drawAction)

        }

    },

需要注意的事项:

① 首先为了适应不同的屏幕,需要获取到实际画点的相对于宽高的映射点。

② 本地测试与网络同步数据可根据自己项目做相应的处理。

③ 也可在本地搭建webSocket去模拟实际服务器传输,可以看这里

4、其他处理(比如清屏、橡皮擦)

// 清屏
    clearAll: function() {

        this.group.ctx.clear();
        this.group.children = [];
        this.isClearMode = false;

        // 初始化清屏的数据
        this.dataDict = {};
        this.dataDict.dataEvent = 'clear';
        let sendData = this.dataDict;

        // 本地测试同步数据
        // this.lookDraw.startDraw(sendData);

        // 网络同步数据
        if (window.room_user) {

            var drawAction = gameAction.getDrawDataAction(window.room_user, sendData)
            happySocket.sendData(drawAction)

        }
    },

    // 橡皮擦
    rubber: function() {

        this.isClearMode = true;
    },

需要注意的事项:

① 清屏的action会放入看板事件队列中,在画完最后一条线后执行。具体看看板代码。

三、看板核心代码

1、初始化参数(要与画板保持一致)

   onLoad: function () {

        // 初始化数据
        this.lineWidth = 5;
        this.strokeColor = cc.color(0, 0, 0);
        this.isClearMode = false;
        this.group = this.addComponent('R.group');
        this.time = 3.0;
        this.duration = 1.0;
        this.pathArray = [];

        // 监听画板数据
        happyDispatch.addEventListener('happyAction', this.receiveData, this);

    },

需要注意的事项:

this.pathArray 为画线的事件队列。

2、开始画线

 // 开始画线
    startDraw: function (dataDict) {
        // 判断是否是清屏
        if (dataDict.dataEvent === 'clear') {

            this.pathArray.push(dataDict);
            return;
        }

        // 初始化线
        let path = this.group.addPath();
        path.strokeColor = cc.color(0, 0, 0).fromHEX(dataDict.strokeColor);
        path.lineWidth = dataDict.lineWidth;
        path.fillColor = 'none';

        // 映射还原点
        var pathPoints = dataDict.points;
        var userPoints = [];
        for (var i = 0; i < pathPoints.length; i++) {

            let pointDict = pathPoints[i];
            var point = cc.p(pointDict.x * this.node.width, pointDict.y * this.node.height);
            userPoints.push(point);
        }

        // 画线并隐藏
        path.points(userPoints.reverse())
        var pathLength = path.getTotalLength();
        path.dashOffset = pathLength;
        path.dashArray = [pathLength];

        // 设置path字典
        var pathDict = {};
        pathDict.path = path;
        pathDict.duration = (dataDict.endTime - dataDict.startTime) / 1000.0;

        // 将path字典存入数组       
        this.pathArray.push(pathDict);
    },

需要注意的事项:

① 清屏数据结构与画线不同所以特殊处理。

② 线的动画显示,实质上是已经画了,不过被隐藏了,只不过是在一定时间里按比例显示出来。

3、处理画线队列(在update


update: function (dt) {

        // 时间递增
        this.time += dt;

        // 设置显示比例
        let percent = this.time / this.duration;

        // 显示比例超过1 更新到队列中的下一条线
        if (percent > 1) {

            // 假如队列有path,排除没画线时出错
            if (this.pathArray.length > 0) {

                // 假如是清屏命令 直接清屏退出此次update
                if (this.pathArray[0].dataEvent === 'clear') {

                    this.clearAll();
                    this.pathArray.shift();
                    return;
                }

                // 比较是否是当前path,是的话移除
                if (this.path === this.pathArray[0].path) {

                    this.pathArray.shift();
                }
            }

            // 在移除之后(或者第一次) 判断队列还有没有path,有的话继续画
            if (this.pathArray.length > 0) {

                // 假如是清屏命令 直接清屏退出此次update
                if (this.pathArray[0].dataEvent === 'clear') {

                    this.clearAll();
                    this.pathArray.shift();
                    return;
                }

                // 开始新一条线的显示(初始化)
                this.path = this.pathArray[0].path;
                this.pathLength = this.path.getTotalLength();
                this.time = 0;
                this.duration = this.pathArray[0].duration;
                percent = 0;
            }
            return;
        }

        // 根据时间刷新画笔的显示
        this.path.dashOffset = this.pathLength * (1 - percent);
        this.path._dirty = true;
    },
});

四、相关资源

1、raphael_github

2、raphael_demo

需要注意的事项:

① 按照raphael_demo中的安装教程安装时,记得后两步即:git submodule update --initnpm install需要在终端cd到项目所在的文件夹执行,不是全局的。
.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,590评论 18 139
  • 越想 越失眠 越想 越不能寐
    寻水的鱼1阅读 164评论 0 0
  • 春来夏至百花芬, 绿意葱茏嬉耍温。 韵色微醺情景醉, 缤纷世界有约晨。
    六月天气阅读 170评论 14 38