2021SC@SDUSC
这期主要学习的是quark-renderer中的canvas,在下载的js文件中,在目录src下有一个canvas文件夹,其中包含canvas的js文件,在该文件夹中有两个js文件,分别是canvasLayer.js和canvasPainter.js。
首先,这两个文件的关系属于依赖关系,canvasPainter根据canvasLayer来创建画布,这些图像不一定可见,有可能只是用来导出图像的实例。对于系统而言,必须存在至少一个canvas画布。其次,canvas是需要直接操作DOM的,而微信小程序的开发中,程序员不能直接操作DOM,因此,在微信小程序的开发中,不能动态的创建canvas。
在canvasLayer.js 中,主要的内容就是在js文件中编写了一个canvasLayer类,然后将其导出。在该类中,首先是构造器constructor函数,如图所示:
在该函数中有四个参数,分别是host、height、width、dpr,其中第一个参数host表示的是HTMLDOM中的元素,例如DIV标签,也可以是canvas实例,或者是context实例,因为在某些运行环境中,不能获得canvas实例的引用,只能获得context。第二个参数width和第三个参数height分别表示宽和高。最后一个参数是dpr,对于dpr,我起初也不知道是什么,感觉应该是一些描述画布的属性的参数,然后我通过结合上下文以及查阅资料,了解到了什么是dpr,网上说dpr是设备像素比,具体理解为在默认缩放为100%的情况下,设备像素和CSS像素的比值,它的全称为device pixel ratio,即物理像素与逻辑像素的比值,在后续的方法中需要用到dpr作为单位来进行求解不同屏幕中的像素问题。
在构造器中,首先写了this.id = guid(),生成唯一id,在utils文件夹下的guid.js中有相应的方法
export default function uuid() {let seed = Date.now();let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {let r = (seed + Math.random() * 16) % 16 | 0;seed = Math.floor(seed / 16);return (c === 'x' ? r : r & (0x3 | 0x8)).toString(16);});return uuid;
}
在该函数中,使用了date的now()函数来返回返回自 1970 年 1 月 1 日 00:00:00 (UTC) 到当前时间的毫秒数。同时又通过Math类的random()函数和floor()函数两者结合来创建一个唯一id。
在确定完唯一id以后,对height、width和dpr这三个参数进行存储, 而this.ctx是canvas绘图上下文,此属性在调用initContext()方法之后初始化,初始化之前此属性为null。
/*** @property {Number} width*/this.width = width;/*** @property {Number} height*/this.height = height;/*** @property {Number} dpr*/this.dpr = dpr;this.ctx;
然后创建canvasInstance,并设置为null,并且使用if语句判断host的值,是否为canvas或者是否为字符串string,亦或者是否为context,然后根据host不同的表示对象,确定canvasInstance或者ctx的值,如下面代码端显示,如果host为canvas,则让canvasInstance = host,并且id也是canvasInstance的id;如果host为一个string类型,则给canvasInstance创建一个canvas,并且使host、width、height、dpr还是传进来的属性参数;如果host是其他,即为context实例,则让this.ctx = host,这样就可以确定实例对象,方便后续操作。
let canvasInstance = null;if (host && host.nodeName && host.nodeName.toUpperCase() === 'CANVAS') {// host is a canvas instancecanvasInstance = host;this.id = canvasInstance.id;} else if (typeof host === 'string') {// host is an id stringcanvasInstance = canvasUtil.createCanvas(host, this.width, this.height, this.dpr);this.id = host;} else {// host is a Context instancethis.ctx = host;}
由于node.js中可能没有canvasInstance的属性,因此需要if判断,是否存在该属性样式,同时,对于页面是否被选择需要确定,使页面不被选择,避免不必要的麻烦,并在canvasInstance的style中修改默认样式,是否被选择、颜色、外边距、内边距以及边界宽度等属性值。最后将canvasInstance赋值给this,注意此时的canvasInstance可能是null,如果host为context的话。而后设置隐藏画布,因为有些实例情况需要使实例隐藏。对于context同样也需要隐藏属性。
if (canvasInstance && canvasInstance.style) {canvasInstance.onselectstart = () => {return false;};canvasInstance.style['-webkit-user-select'] = 'none';canvasInstance.style['user-select'] = 'none';canvasInstance.style['-webkit-touch-callout'] = 'none';canvasInstance.style['-webkit-tap-highlight-color'] = 'rgba(0,0,0,0)';canvasInstance.style['padding'] = 0; // eslint-disable-line dot-notationcanvasInstance.style['margin'] = 0; // eslint-disable-line dot-notationcanvasInstance.style['border-width'] = 0;
}
this.canvasInstance = canvasInstance;
this.hiddenCanvas = null;
this.hiddenContext = null;
在往后看,设置初始颜色,将动态模糊(运动模糊是景物图像中的移动效果)改为false,并将在开启动态模糊的时候,将Alpha(与上一帧的混合程度)设置为0.7,并将增量渲染(接着上一帧的画面继续绘制)设置为false,对于标志位__dirty 将其设置为false,可以在后续绘制canvas时节约大量时间。其他的一些index都默认设置为0。
this.clearColor = 0;this.motionBlur = false;this.lastFrameAlpha = 0.7;this.incremental = false;this.__dirty = true;
this.__used = false;
this.__drawIndex = 0;
this.__startIndex = 0;
this.__endIndex = 0;
这主要是构造函数中的一些内容,由于刚上手,分析起来需要查阅很多相关资料,一些语句关联着其他js文件,解读比较繁琐。后续继续分析后面的代码段。