欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JS 浅拷贝——Shallow copy 在 Canvas 绘图中的应用

程序员文章站 2022-04-10 19:00:16
(一)简述 JavaScript 浅拷贝 和 深拷贝在介绍浅拷贝和深拷贝的概念之前,我们不妨先了解一下 JavaScipt 变量的两种保存方式。ECMAScript 变量可能包含两种不同数据类型的值:基本类型值和引用类型值。基本类型值指的是简单的数据段,而引用类型值指那些可能由多个值构成的对象。在将一个值赋给变量时,解析器必须确定这个值是基本类型值还是引用类型值。5 种基本数据类型:Un......

(一)简述 JavaScript 浅拷贝 和 深拷贝

在介绍浅拷贝和深拷贝的概念之前,我们不妨先了解一下 JavaScipt 变量的两种保存方式。

ECMAScript 变量可能包含两种不同数据类型的值:基本类型值引用类型值。基本类型值指的是简单的数据段,而引用类型值指那些可能由多个值构成的对象。在将一个值赋给变量时,解析器必须确定这个值是基本类型值还是引用类型值。5 种基本数据类型:Undefined、Null、Boolean、Number 和 String。这 5 种基本数据类型是按值访问的,因为可以操作保存在变量中的实际的值。引用类型的值是保存在内存中的对象。与其他语言不同,JavaScript 不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。为此,引用类型的值是按引用访问的

1.1 浅拷贝 Shallow copy

  • 对于基本类型的值,进行浅拷贝时,将一个变量复制给另一个变量,相当于创建了一个新值,两个变量是独立的,不会互相影响。举个栗子:
var a = 2;
var b = a;
a = 5;
console.log(a); //5
console.log(b); //2
  • 对于引用类型值,进行浅拷贝时,实际上只是将操作的对象的引用复制给另一个变量,两个变量指向同一个内存中的变量,修改其中任意一个变量将影响另一个变量,思考下面的例子:
var obj1 = new Object({'name' : 'John', 'age' : 19});
var obj2 = obj1;
obj1.name = 'Sara';
console.log(obj1); //{'name' : 'Sara', 'age' : 19}
console.log(obj2); //{'name' : 'Sara', 'age' : 19}

obj2.age = 30;
console.log(obj1); //{'name' : 'Sara', 'age' : 30}
console.log(obj2); //{'name' : 'Sara', 'age' : 30}

上面例子中,无论修改任何一个变量,都会直接修改这两个变量共同指向的内存中的对象,因此,两个变量的值同时改变。

(二)浅拷贝在Canvas绘图中的应用

我们使用Canvas绘图时,经常会遇到改变所绘图形的属性问题,比如控制位置的坐标点。使用浅拷贝,可以有效地节省代码,减少遍历次数,提高代码执行效率。

下面的例子将重点展示:浅拷贝在拖拽图形时如何改变图形的坐标属性。

JS 浅拷贝——Shallow copy 在 Canvas 绘图中的应用

JS 浅拷贝——Shallow copy 在 Canvas 绘图中的应用

 如上图所示,图中有三个多边形,我们移动其中一个红色的多边形,改变了红色多边形的坐标属性。

其中的逻辑是:当鼠标点击canvas画布时,判断鼠标是否落在多边形范围内,如果是,则找出这个多边形;并且将这个多边形对象浅拷贝给一个变量dragging;当鼠标移动时,实时改变dragging变量中的x,y属性,此时多边形对象的属性x, y也同时改变。

我们观察其中一个代码片段,思考其中应用的浅拷贝的原理:

下面代码节选自《Core HTML5 Canvas Graphics, Animation, and Game Development》。

// Event handlers.....................................................

//鼠标点击画布时...
canvas.onmousedown = function (e) {
   var loc = windowToCanvas(e.clientX, e.clientY); //获取当前鼠标位置

   e.preventDefault(); // prevent cursor change

   if (editing) { //进入编辑模式...
     polygons.forEach( function (polygon) { //遍历所有的多边形对象组成的数组polygons
        polygon.createPath(context); //绘制路径,但不描边
        if (context.isPointInPath(loc.x, loc.y)) { //判断鼠标位置是否落在多边形范围内
           saveDrawingSurface(); //储存画布信息
           mousedown.x = loc.x;  //储存鼠标信息
           mousedown.y = loc.y;

           dragging = polygon; //将当前选中的多边形对象浅拷贝给dragging
           draggingOffsetX = loc.x - polygon.x; //储存鼠标距离多边形中心点的偏移值
           draggingOffsetY = loc.y - polygon.y;
           return;
        }
     });
   }
   else { //进入绘图模式...
     startDragging(loc);
     dragging = true;
   }
};

//鼠标移动时......
canvas.onmousemove = function (e) {
   var loc = windowToCanvas(e.clientX, e.clientY);

   e.preventDefault(); // prevent selections

   if (editing && dragging) { //进行编辑模式并选中多边形
      dragging.x = loc.x - draggingOffsetX; //修改dragging.x值,直接修改了选中多边形的x属性
      dragging.y = loc.y - draggingOffsetY; //修改dragging.y值,直接修改了选中多边形的y属性

      context.clearRect(0, 0, canvas.width, canvas.height); //清除画布
      drawGrid('lightgray', 10, 10); //绘制网格线
      drawPolygons(); // 重新绘制所有的多边形
   }
   else { //略过......
     if (dragging) {
        restoreDrawingSurface();
        updateRubberband(loc, sides, startAngle);

        if (guidewires) {
           drawGuidewires(mousedown.x, mousedown.y);
        }
     }
   }
};

将选中的多边形对象复制给dragging变量,当修改dragging变量时,同时也改变了选中多边形的属性,节省了代码,避免了多次遍历数组,提高代码执行的效率。

 完整示例请查阅Github源码:https://github.com/corehtml5canvas/code/tree/master/ch02/example-2.28

 

 

 

 

本文地址:https://blog.csdn.net/m0_37128507/article/details/85785373