Canvas实现虚拟元素拖拽

CanvasRenderingContext2D API

Syntax

boolean ctx.isPointInPath(x, y);
boolean ctx.isPointInPath(x, y, fillRule);

boolean ctx.isPointInPath(path, x, y);
boolean ctx.isPointInPath(path, x, y, fillRule);

MDN Canvas isPointInPath参考

参数:
x: 待检查的点的x坐标。
y: 待检查的点的y坐标。
fillRule: 一种判断一个点位于路径内部还是路径外部的算法,可用的属性值有默认的"nonzero"(非零环绕)和"evenodd"(奇偶环绕)两种。

参考:

The non-zero winding rule

The even-odd winding rule

帅华君画了两幅图辅助理解“非零环绕”与“奇偶环绕”规则。

【↓奇偶环绕规则↓】

非零环绕原则 陈帅华


自己写一个构造器函数 Object2D

threeJs里有一个Object3D类,于是乎帅华君也照猫画虎的写了一个Object2D类,代码如下:

object2d.js

function Object2D(o){
  this.x = 0;
  this.y = 0;
  this.size = o.size;
  this.style = 'hsla('+Math.ceil(Math.random()*360)+', '+(70+Math.ceil(Math.random()*30))+'%, 60%, 1)';
  this.name = '';
  this.id = new Date().getTime();
  this.zIndex = 0;
  this.needFill = false;
  this.rayActived = o.rayActived;
  this.animationQueue = [];
  this.angle = 0;
}
Object2D.prototype = {
  toBig: function(){
    var des = this.size + 4;
    this.animationQueue.push({
      property: 'size',
      originSize: this.size,
      des: 20,
      progress: 0,
      speed: 0.005,
      speedA: 0.03,
    });
  },
  update: function(){
    this.angle += Math.PI/180 * .6;

    var aniqueue = this.animationQueue;
    if(!aniqueue.length){ return; }
    for(var i=0; i<aniqueue.length; i++){
      if(aniqueue[i].progress>=1){
        this[aniqueue[i].property] = aniqueue[i].originSize + aniqueue[i].des;
        aniqueue.splice(aniqueue[i], 1);
        continue;
      }
      aniqueue[i].speed += aniqueue[i].speedA;
      aniqueue[i].progress += aniqueue[i].speed;
      this[aniqueue[i].property] = aniqueue[i].originSize + aniqueue[i].des * aniqueue[i].progress;
    }

  },
  resPath: function(ctx){
    ctx.save();
    ctx.translate(this.x, this.y);
    ctx.rotate(this.angle);
    ctx.beginPath();
    ctx.arc(0, 0, this.size, 0, Math.PI*2, true);
    ctx.restore();
  },
  draw: function(ctx){
    this.update();
    ctx.save();
    this.resPath(ctx);
    if(this.needFill){
      ctx.fillStyle = this.style;
      ctx.fill();
    }else{
      ctx.strokeStyle = this.style;
      ctx.setLineDash([6,6]);
      ctx.lineWidth = 1;
      ctx.stroke();
    }
    ctx.restore();

    ctx.save();
    ctx.translate(this.x, this.y);
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    if(this.needFill){
      ctx.fillStyle = '#fff';
    }else{
      ctx.fillStyle = this.style;
    }
    ctx.font = 'normal 900 24px "微软雅黑"';
    ctx.fillText(this.name, 0, 0);
    ctx.restore();
  }
};

帅华君还写了一个用于捕获鼠标事件(移入、移出、点击时间)的类Raycast,同样也是学习了threeJS。

raycast.js

function Raycast(all, e){
  this.activedObject2Ds = [];
  this.allObject2D = all;
  this.event = e;
  this.eventX = e.clientX;
  this.eventY = e.clientY;
  this.activedObject2D;
}
Raycast.prototype = {
  getDestination: function(callback){
    this.activedObject2Ds = [];
    var m;
    var a;
    var type = this.event.type;
    var all = this.activedObject2Ds;
    for(var i=0; i<this.allObject2D.length; i++){
      var object2d = this.allObject2D[i];
      object2d.resPath(ctx);
      if(ctx.isPointInPath(this.eventX, this.eventY)){
        this.activedObject2Ds.push(object2d);
        if(all.length == 0){ return; }
        if(all.length == 1){
          a = all[0];
        }else{
          for(var i=0; i<all.length; i++){
            var o = this.activedObject2Ds[i];
            if(!m){ m = o.zIndex; a = o; continue; }
            if(o.zIndex >= m){
              m = o.zIndex;
              a = o;
            }
          }
        }
      }
    }
    if(a){
      callback(type, a);
    }else{
      callback();
    }
  },
};

main.js

var canvas = document.createElement('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.width = window.innerWidth;
canvas.style.height = window.innerHeight;
var ctx = canvas.getContext('2d');
document.body.appendChild(canvas);

var wait_render_data = [{},{},{},{},{},{},{},{},{},{},{},{},{},{}];
var allObject2D = [];
// ------------------------------------
var rayActived = [];

for(var i=0; i<wait_render_data.length; i++){
  var ball = new Object2D({
    rayActived: rayActived
  });
  ball.x = Math.floor(window.innerWidth * Math.random());
  ball.y = Math.floor(window.innerHeight * Math.random());
  ball.size = 40+Math.ceil(Math.random()*60);
  ball.name = 'ball_'+i;
  ball.zIndex = i;
  allObject2D.push(ball);
}
(function render(){
  requestAnimationFrame(render);
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  for(var i=0; i<wait_render_data.length; i++){
    allObject2D[i].draw(ctx);
  }
}());

var isMouseDown = false;
var activeObject;
var offsetX;
var offsetY;
canvas.addEventListener('mousedown', function(e){
  isMouseDown = true;
  var raycast = new Raycast(allObject2D, e);
  raycast.getDestination(function(type, obj){
    if(!obj) return;
    activeObject = obj;
    offsetX = activeObject.x - e.clientX;
    offsetY = activeObject.y - e.clientY;
    obj.needFill = true;
  });
}, false);
canvas.addEventListener('mousemove', function(e){
  if(!isMouseDown) return;
  var raycast = new Raycast(allObject2D, e);
  raycast.getDestination(function(type, obj){
    if(!obj) return;
    activeObject.x = e.clientX + offsetX;
    activeObject.y = e.clientY + offsetY;
  });
}, false);
canvas.addEventListener('mouseup', function(e){
  isMouseDown = false;
  for(var i=0; i<allObject2D.length; i++){
     allObject2D[i].needFill = false;
  }
}, false);
window.addEventListener('resize',function(){
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
  canvas.style.width = window.innerWidth;
  canvas.style.height = window.innerHeight;
},false);


index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>canvas</title>
    <link rel="stylesheet" href="main.css">
  </head>
  <body>

  </body>
  <script type="text/javascript" src="object2d.js"></script>
  <script type="text/javascript" src="raycast.js"></script>
  <script type="text/javascript" src="main.js"></script>
</html>

下一篇《搞定微信公众平台开发者基本配置》

上一篇《巧用Illustrator与CSS3制作SVG动画》

永久链接 http://www.shuaihua.cc/article/drag-virtual-element-use-canvas-in-html5-standard

快速跳转 心头好文 - javascript - 《Canvas实现虚拟元素拖拽》

发布日期 2017年9月1日 星期五

版权声明 自由转载-非商用-非衍生-保持署名(创意共享3.0许可证