H5 CANVAS绘图轨迹回放

最近实现了一个Canvas轨迹回放功能,产品需求:可以在图片上进行留痕操作,并且是多张图片,操作完成后数据提交到服务器,客户端获取数据后,对Canvas的操作轨迹进行回放,还原图片留痕的操作过程,并且可以配上语音进行解说。以下代码仅为Demo,实现重绘功能,业务代码比较多,就不进行分享了。
以下是回放代码,由于产品要求的时间比较紧,其中部分代码参照了网上实例,代码如下:

        function getBodyOffsetTop(el) {
            var top = 0;
            do {
                top = top + el.offsetTop;
            } while (el = el.offsetParent);
            return top;
        }
        function getBodyOffsetLeft(el) {
            var left = 0;
            do {
                left = left + el.offsetLeft;
            } while (el = el.offsetParent);
            return left;
        }
        function Drawing(canvas, options) {
            typeof canvas == 'string' && (canvas = document.getElementById(canvas));
            if (!canvas || !canvas.getContext) {
                throw new Error(100, '你的浏览器不支持!');
            }
            this.starttime = 0;
            this.init(canvas);
        }
        Drawing.prototype = {
            init: function (canvas) {
                this.canvas = canvas;
                this.context = canvas.getContext('2d');
                this.context.lineWidth = 1;
                this.context.lineJons = 'round';
                this.context.lineCep = 'round';
                this.isButtonDown = false;
                this.historyStroker = [];
                this.curStroker = { color: '#000000', path: [] };
                this.lastX = 0;
                this.lastY = 0;
                this.curColor = '#000000';
                this.toolbarspos = {};
                this.bindEvent();
            },
            bindEvent: function () {
                var self = this;
                this.canvas.addEventListener('mousemove', function (event) {
                    var x = event.pageX - getBodyOffsetLeft(this),
                    y = event.pageY - getBodyOffsetTop(this);
                    self.onMouseMove({ x: x, y: y });
                }, false);
                this.canvas.addEventListener('mousedown', function (event) {
                    var x = event.pageX - getBodyOffsetLeft(this),
                    y = event.pageY - getBodyOffsetTop(this);
                    self.onMouseDown(event, { x: x, y: y });
                }, false);
                this.canvas.addEventListener('mouseup', function (event) {
                    var x = event.pageX - getBodyOffsetLeft(this),
                    y = event.pageY - getBodyOffsetTop(this);
                    self.onMouseUp(event, { x: x, y: y });
                }, false);
                this.canvas.addEventListener('click', function (event) {
                    var x = event.pageX - getBodyOffsetLeft(this),
                    y = event.pageY - getBodyOffsetTop(this);
                    self.onClick({ x: x, y: y });
                }, false);
            },
            onMouseMove: function (pos) {
                if (this.isButtonDown) {
                    var p = this.toolbarspos;
                    for (var i in p) {
                        if (pos.x >= p[i].x
                        && pos.x <= p[i].x + p[i].w
                        && pos.y >= p[i].y
                        && pos.y <= p[i].y + p[i].h) {
                            return;
                        }
                    }
                    this.context.lineTo(pos.x, pos.y);
                    this.context.stroke();
                    this.lastX = pos.x;
                    this.lastY = pos.y;
                    pos.timer = (new Date()).getTime() - this.starttime;
                    this.curStroker.path.push(pos);
                }
            },
            onMouseDown: function (event, pos) {
                if (this.starttime == 0) {
                    alert("请点击开始绘制按钮进行绘制");
                    return;
                }

                if (event.button == 0) {
                    var p = this.toolbarspos;
                    for (var i in p) {
                        if (pos.x >= p[i].x
                        && pos.x <= p[i].x + p[i].w
                        && pos.y >= p[i].y
                        && pos.y <= p[i].y + p[i].h) {
                            return;
                        }
                    }
                    this.isButtonDown = true;
                    this.lastX = pos.x;
                    this.lastY = pos.y;
                    this.context.beginPath();
                    this.context.moveTo(this.lastX, this.lastY);
                    this.curStroker.color = this.curColor;
                    pos.timer = (new Date()).getTime() - this.starttime;
                    this.curStroker.path.push(pos);
                }
            },
            onMouseUp: function (event, pos) {
                if (event.button == 0) {
                    var p = this.toolbarspos;
                    for (var i in p) {
                        if (pos.x >= p[i].x
                        && pos.x <= p[i].x + p[i].w
                        && pos.y >= p[i].y
                        && pos.y <= p[i].y + p[i].h) {
                            return;
                        }
                    }
                    this.isButtonDown = false;
                    this.historyStroker.push(this.curStroker);
                    this.curStroker = { color: this.curColor, path: [] };
                }
            },

            ResetDrawCentre: function () {
                var p = this.historyStroker, p2,
                curColor = this.context.strokeStyle;
                for (var i = 0; i < p.length; i++) {
                    this.context.strokeStyle = p[i].color;
                    this.context.beginPath();
                    for (var t = 0; t < p[i].path.length; t++) {
                        p2 = p[i].path[t];
                        if (t == 0) this.context.moveTo(p2.x, p2.y);
                        this.context.lineTo(p2.x, p2.y);
                        this.context.stroke();
                    }
                    this.context.beginPath();
                }
                this.context.strokeStyle = curColor;
            },
            testDraw: function () {
                var self = this;
                //this.intervalHandle = null;
                if (!this.startplay) {
                    this.startplay = (new Date()).getTime();
                }
                if (!this.lineIndex) {
                    this.lineIndex = 0;
                }
                var p = this.historyStroker;
                var curLine = p[this.lineIndex], curColor = p[0].color, pointIndex = 0, p2;
                if (!this.testcanvas) {
                    this.testcanvas = document.getElementById("testcan");
                    this.testcontext = this.testcanvas.getContext('2d');
                    this.testcontext.beginPath();
                }

                if (this.intervalHandle) return;
                this.intervalHandle = window.setInterval(function () {
                    var deltatime = (new Date()).getTime() - self.startplay;
                    p2 = p[self.lineIndex].path[pointIndex];
                    //绘制完成一条线
                    if (!p2) {
                        window.clearInterval(self.intervalHandle);
                        self.intervalHandle = null;
                        if (self.lineIndex < p.length - 1) {
                            self.lineIndex = self.lineIndex + 1;
                            self.testDraw();
                            console.log("===================")
                        }

                    }
                    if (deltatime >= p2.timer) {
                        self.testcontext.strokeStyle = curColor;

                        if (pointIndex == 0) {
                            self.testcontext.moveTo(p2.x, p2.y);
                            console.log(0);
                        }
                        pointIndex = pointIndex + 1;
                        self.testcontext.lineTo(p2.x, p2.y);
                        self.testcontext.strokeStyle = "#ff0000";
                        self.testcontext.stroke();
                        //testcontext.beginPath();
                        console.log(p2.x + "===" + p2.y);
                    }
                }, 0)

            }
        };
        var draw = new Drawing('can');
        $("#startdraw").click(function () {
            draw.starttime = (new Date()).getTime();
        })
        $("#testdraw").click(function () {
            console.log(draw.historyStroker);
            draw.testDraw();
        })

实现原理:首先要记录开始绘制的时间draw.starttime,它是记录相对时间的起始时间,在移动过程中计算出每一个点的相对时间(毫秒)pos.timer = (new Date()).getTime() - this.starttime;并作为点的一个属性值进行存储,在回放过程中通过setInterval来实现,时间间隔设置为0,从点击回放按钮开始计算相对时间,如果相对时间大于或者等于某一个点的timer时,(注意:这里必须设置为大于或者等于,因为在有些浏览器里,由于setInterva内部执行效率不同,毫秒级别的时间点并不会连续,仅判断等于会不准确),绘制该点坐标,依次类推,当绘制完一条线时,进行递归操作,绘制第二条线,效果如下图所示:
![H5 canvas轨迹重绘]
H5 CANVAS绘图轨迹回放_第1张图片
Demo地址:https://github.com/mazhaohai/Play-H5-Canvas
如果需要进行图片分页,还需要给没一个点坐标添加pageid,即属于哪一张图片,如果绘制过程中图片进行了切换,需要重新设置draw.starttime,每一张图片都有自己的起始时间点;在回放过程中,如果需要多张图片自动切换,需要在图片预加载成功之后,重新设置每张图片的回放起始时间,再进行点的相对时间比对,否者会有时间差,比对不准确,原因是每张图片加载时间不同,每一张图片都要在预加载之后设置绘制起始时间和回放起始时间。

你可能感兴趣的:(web前端开发)