canvas 基本应用开发

14 篇文章 0 订阅

前段时间想深入了解一下canvas,就写了一个canvas库.这段时间在忙其他的怕之前写的东西忘记了,特意想在这上面记一下。我不会一下写完,会慢慢更新。

先看一段代码:

 // 创建一个画布渲染器
        var _DxCanvas = new DxRender('#test', {
            width: 1500,
            height:1500,
            layers: [new CanvasLayer()]
        });
  
        // 绘制一个正方形框
        var rect = new RectShape({
            x: 200,
            y: 200,
            width: 500,
            height: 500,
            style: new Style({
                lineWidth: 1,
                strokeStyle: '#000'
            })
        })
        _DxCanvas.add(rect);

        var beziers = [.25, .1, .25, 1];
        var getBezierFormaul = function () {     
            var w = rect.options.width, h = rect.options.height, x = rect.options.x, y = rect.options.y;
            return [beziers[0] * w + x, (1 - beziers[1]) * h + y, beziers[2] * w + x, (1 - beziers[3]) * h + y, x+w, y];
        }
        var currentBezier = getBezierFormaul();
        // 更新轨迹线
        var bezierFormula = function () {
              line.set('points[0]', getBezierFormaul())
        }
        showbeziers();
        // 创建轨迹线
        var line = new BezierShape({
            x: rect.options.x,
            y: rect.options.y+rect.options.width,
            points: [
               getBezierFormaul() // 获取轨迹线的绘制路径
            ],    
            style: new Style({
                lineWidth: 3,
                strokeStyle:'red'
            })
        });
        // 创建贝塞尔线第一个控制点
        var controlCircle = new CircleShape({
            x: currentBezier[2],
            y: currentBezier[3],
            r: 10,
            style: new Style({
                fillStyle: 'red'
            })
        });
        // 创建贝塞尔线第二个控制点
        var controlCircle2 = new CircleShape({
            x: currentBezier[0],
            y: currentBezier[1],
            r: 10,
            style: new Style({
                fillStyle: 'red'
            })
        });

        //  跟随轨迹线运动圆点
        var controlCircle3 = new CircleShape({
            x: 100,
            y: 600,
            r: 10,
            style: new Style({
                fillStyle: 'green'
            })
        });
        // 给图形绑定事件
        function bindEvent(element,type)
        {
            var controlEvent = {
                isDown: false,
                left: 0,
                top: 0,
                x: null,
                y: null,
                prev: null
            };
            element.on('mousedown', function (e) {
                controlEvent.isDown = true;
                controlEvent.left = element.get('x');
                controlEvent.top = element.get('y');
                controlEvent.x = e.x;
                controlEvent.y = e.y;
            })

            _DxCanvas.on('mousemove', function (e) {
                if (!controlEvent.isDown) {
                    return;
                }
                var cx = e.x, cy = e.y;
                var px = cx - controlEvent.x, py = cy - controlEvent.y;
                var nX = controlEvent.left + px;
                var nY = controlEvent.top + py;
   
                var maxW = rect.options.width;
                var maxH = rect.options.height;
                var left=rect.options.x;
                var top = rect.options.y;

                var dx = element.get('x'), dy = element.get('y');


                if (type == 1) {
                    element.set('x', nX);
                    beziers[2] = toFixed((nX - left) / maxW,2);
                    element.set('y', nY);
                    beziers[3] =toFixed( 1-(nY - top) / maxH,2);
                } else {
                    element.set('x', nX);
                    beziers[0] = toFixed((nX - left) / maxW, 2);
                    element.set('y', nY);
                    beziers[1] = toFixed(1 - (nY - top) / maxH, 2);
                }

                bezierFormula();
                showbeziers();
                controlEvent.prev = { x: cx, y: cy };

            })
            _DxCanvas.on('mouseup', function (e) {
                console.log('mouseup');
                controlEvent.isDown = false;
            })
        }
        
        // 第一个控制点绑定移动事件
        bindEvent(controlCircle,1)
        // 第二个控制点绑定移动事件
        bindEvent(controlCircle2, 2)

        // 添加图形,到图层中
        _DxCanvas.add(line);
        _DxCanvas.add(controlCircle);
        _DxCanvas.add(controlCircle2);
        // 点击开始,运动点按着轨迹线开始运动
        document.getElementById('start').addEventListener('click', function () {
            _DxCanvas.add(controlCircle3);       
            // 增加一个动画
            _DxCanvas.animation.addAnimator(new Animator({
                x: 100,
                y: 600
            }, {
                onFrame: function (target, p) {
                    var beziers = getBezierFormaul();
                    var o = bezier([[100, 600], [beziers[0], beziers[1]], [beziers[2], beziers[3]], [beziers[4], beziers[5]]], p);
                    controlCircle3.set('x', o.x);
                    controlCircle3.set('y', o.y);    
                },
                onDone:function()
                {
                    controlCircle3.remove();// 动画结束后,移动运动圆点
                  
                }
            }).when(3000, {
                x: 500,
                y: 0
            }).start())

        });
  

先讲一下

  1. Element  存储图形属性基类
  2. Style 设置图片样式
  3. Text  文本
  4. Geometry 所有图形基类,它会继承(Element,Transformable,Animatable)
  5. Animation ,Animatable 动画引擎,图形动画
  6. Transformable 图形变换(位移、旋转、缩放等)
  7. Matrix 矩阵算法
  8. layer 图层
  9. render 图层渲染器

先讲一下Element 类

Element.extend

它的作用:主要是定义图形应用属性,监听属性变化。比如:上面代码中图形可以通过.set()可以改变图形属性,从而刷新图形.

init方法:会把图形上面定义的默认属性,扩展到实例对象自身或者options上面.默认是扩展在options对面上面

set(path,value,slient) 方法:

注意path它不是key。这两个不同key是一个属性名,path是属性路径。path支持set('data.list[0].x',x)

slient沉默。当为true就不刷新图形,只是简单修改属性。

refresh(sync)方法: sync 是同步更新

这段代码主要的作用是:

that.__refersh的作用:

假如:在不同函数中,有很多地方在操作A图形的属性。例如A.set('x',x),A.set('y',y)。那么我不管它操作多少次,我只会通知渲染器更新一次

scheduler.nextTick的作用:

假如:我们canvas上面,有上百个图形,发生了属性变化,那么我们不可能每个图形变化了,就去让canvas渲染器去渲染一遍,那通过这个调度的机制,先收集当前所有发生变化的图形放到队列中,等到所有代码执行完,再去通知渲染器去渲染,这个就是类似vue的nextTick

 // 保存多次调用刷新时,只触发一次
        if (!that.__refresh) {
            that.__refresh = true;// 刷新中
            scheduler.nextTick(function () {
                that.__refresh = false;// 还原
                that.emit('change');
            });
        }

 

 

下面看着代码讲解:

var Element = Observable.extend({
    defaultOptions: {},
    isExtendOptions:true,
    init:function(options)
    {
        Observable.fn.init.call(this);
        if (this.isExtendOptions) {
            this.options = extend(true,{}, this.defaultOptions);
        } else {
            extend(true,this, this.defaultOptions);
        }
        this.setOptions(options,true);
        this.__refresh = false;
    },
    _resolvePath:function(obj,path)
    {
        var paths = path.match(PATH_REGEXP), key = path, i, len, current=obj, result = {
            key: '',
            current:null
        };
        if (paths && paths.length > 1) {
            key = paths.pop();
            len = paths.length;
            i = -1;
            if (key.charAt(0) == '[') {
                key = key.match(PATH_KEY_REGEXP)[0]
            }
            while (++i < len && current) {
                current = current[paths[i]];
            }
        }
        result.key = key;
        result.current = current;
        return result;
    },
    setOptions:function(options,slient)
    {
        if (this.isExtendOptions ) {
            extend(this.options, options);
        } else {
            extend(this, options);
        }
        if(!slient)
        {
            this.refresh();
        }
    },
    onChange:function(name,handler)
    {
        if (isFunction(name)) {
            handler = name;
            name = null;
            this.on('change', handler);
        } else {
            this.on('change:'+name, handler);
        }
    },
    removeChange: function (name, handler)
    {
        if (isFunction(name)) {
            handler = name;
            name = null;
            this.off('change', handler);
        } else {
            this.off('change:' + name, handler);
        }
    },
    refresh:function(sycn)
    {
        var that = this;
        if (sycn)
        {
            that.emit('change');
            return;
        }
        // 保存多次调用刷新时,只触发一次
        if (!that.__refresh) {
            that.__refresh = true;// 刷新中
            scheduler.nextTick(function () {
                that.__refresh = false;// 还原
                that.emit('change');
            });
        }
    },
    has: function (path)
    {
        var that = this, paths = this._resolvePath(that.isExtendOptions ? that.options : that, path), key = paths.key, current = paths.current;
        if (!current) {
            return false;
        }
        return current.hasOwnProperty(key);
    },
    _set: function (path, value,slient)
    {
        var that = this, paths = this._resolvePath(that.isExtendOptions?that.options:that, path), key = paths.key, current = paths.current, oldVal;     
        if (!current) {
            return;
        }
        oldVal= current[key];
        if(oldVal===value)
        {
            return;
        }
        current[key] = value;
        if (!slient) {
             that.emit('change:' + path, value,key,path);
        }
    },
    set:function(path, value,slient)
    {
        this._set(path, value, slient);
        if (!slient) {
            this.refresh();
        }
    },
    get: function (path)
    {
        var that = this, paths = this._resolvePath(that.isExtendOptions ? that.options : that, path), key = paths.key, current = paths.current;
        if (!current) {
            return;
        }
        return current[key];
    }
})

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值