先前在第三章画树时,就有发现把树叶画上去时,系统工作时间会增加而导致掉侦,原图是300x300,50kb左右,不是很大,但是我们要画一大堆落叶的话,计算量就会很大,因为影像处理的底层就是去运算300x300个像素点,然後再根据要求的大小,比如50x50,再进行缩放,而context.drawImage
方法每次都会经历这一个步骤,因此重复多次相当花费效能,为此我们有两种方法做搭配:
预估每个叶子最大的大小为100x100像素,我们就依此来裁切原档,在修图时有个重点,因为在Canvas在绘制图形时,都是以左上角为准,如果我们的素材不对齐左上角,就还要额外去做平移和计算,因此为了JS处理具有一致性和方便性,我们把所有叶子的根部都对齐左上:
接着就是基本操作:拉辅助线、旋转缩放、调整版面尺寸
值得注意的是,100x100和300x300的大小可是差了约9倍,相当可观,即使调整微小,也是会省去不少效能,实际上裁切完的大小是:50kb >>> 12kb,接着再进一步拿去影像压缩:
又变得更小,剩下4kb了!不过提醒大家,一定要保留原档,因为所谓的压缩,本质上是一种对於档案的毁损,同时它也失去再修图的可能,只不过是肉眼看不太出来而已:
在树建立之初在Stick的原型上设置min变数为20,如此以来每一个节点都可以由node.min来取得该变数,而不会被其他无关的物件来取用到:Stick.prototype.min = 20;
if(node.r < node.min){
// 在树枝建立之初就有设置过node.img,但为了避免pngImg存有的canvas还未被建立,而重新指向
// 也可以在更源头的地方解决,即图片全部载入完毕,才开始游戏
if(node.img == undefined) node.img = pngImg[1 + Math.floor(random(2))];
else{
context.strokeStyle = 'rgba(120, 215, 140, 1)';
context.save();
context.translate(x, y);
context.rotate(-Math.PI / 4 - theta2);
context.drawImage(node.img, 0, 0,
node.r * node.grow * 1.5,
node.r * node.grow * 1.5);
context.restore();
}
}
在canvas的座标中,不指Y是反过来的,包括角度也是反过来(顺时针为正),一开始原点为图片的左上角,表示叶子面向右下角,即45度(Math.PI/4),因此要先减去该角度,再减去theta2(该节点的生长方向)
压缩图片之後,对效能的确有帮助,但似乎不太够,毕竟大约有近千片树叶等着绘制,在console中可观察到Tree.Draw()花了特别多的时间,其范围为22.74-31.24秒不等,会造成严重的掉侦问题:
因此我们先尝试刚刚说的先将图片进行压缩,放到更小的画布上:
let leafImg = new Array();
let pngImg = new Array();
for(let N = 0; N < 4; N++){
leafImg[N] = new Image();
// 等待每一张图片读取好
leafImg[N].onload = () => {
// 每张图片创建一个对应的画布
pngImg[N] = document.createElement('canvas');
pngImg[N].width = 30;
pngImg[N].height = 30;
let ctx = pngImg[N].getContext("2d");
// 画一次就可以了,以後就拿pngImg[N]来当图片(N为0~3之间)
ctx.drawImage(leafImg[N], 0, 0, 30, 30);
}
}
leafImg[0].src = "../images/tinyPng/Leave2O.png";
leafImg[1].src = "../images/tinyPng/Leave2Y.png";
leafImg[2].src = "../images/tinyPng/LeaveRY.png";
leafImg[3].src = "../images/tinyPng/LeaveRR.png";
// 设定完档案路径网页端才会开始读取,接着触发刚刚写的onload
实际尝试过後,似乎对於效能没有显着的帮助,至少还是需要21.64秒,仅快了1秒左右
不过值得注意的是,不只是树叶花了很多时间,其实当初在设计并绘制树枝时,也浪费了很多效能,如果把绘制树枝的贝兹曲线去掉,只保留节点的计算和树叶,便会发现时间落在11.03-13.34秒左右:
由此可知,演算约1400个树枝花了近10秒,演算约1000个树叶花了近12秒,算是相当接近了,
而只绘制树叶的效能表现也是好很多,因此之後设计游戏的时候,会考虑只有在游戏读取画面时,会有让树长大动画,而在玩游戏时,只有一个静置的树在後面(可能是另外一个canvas),不做重复绘制的动作。
当然,我们也可以把RATIO改回1,事情就会单纯很多:
(更正:由於Ratio改变,min=20就从原本的10px变成20px,使得树叶少递回1~2次,以下两个例子实际节点只有450个,约为原本的1/3)
相较之下,就没什麽效能问题,不过就没有原本想要的高解析度了,毕竟少了Ratio增加画布大小,就只是单纯拿30x30的图片去放大
再想想看怎麽办吧!以下是用RATIO=2绘制出来的,是真的比上面两张图精致好看很多。
今天都就先用现有素材来做,不特别去找红花绿叶了xd,
不知道大家觉得哪个比较好看呢?
<<: Day 31 | 常见 Livewire 问题: jQuery 在渲染时会打回原形
今天邀请到同系同级的 Howard 来分享他在高中学习程序的经历和对於 APCS 的想法~ 程序学习...
接续昨天 header 的部分: If-Modified-Since:只在最近有来源最近有异动时发送...
前一天中我们讲解了如何利用Vue CLI快速建立专案,再进入到专案开发之前,还是有一些知识需要恶补的...
Ruby on Rails为用Ruby程序语言写的开源网页框架,Rails的发明者DHH挑选了Rub...
这篇的上一篇:https://ithelp.ithome.com.tw/articles/10283...