小程序Canvas性能优化实战

栏目: Html5 · 发布时间: 4年前

内容简介:小程序中的canvas性能有限,特别在交互的过程中不断触发重绘会引发严重卡顿。在不考虑优化的情况下,先说说如何实现绘制和交互。首先看看数据,服务返回的数据中每个元素都是独立的,包括该元素的样式及坐标

小程序中的canvas性能有限,特别在交互的过程中不断触发重绘会引发严重卡顿。

基本实现

在不考虑优化的情况下,先说说如何实现绘制和交互。

数据格式

首先看看数据,服务返回的数据中每个元素都是独立的,包括该元素的样式及坐标

// 线路数据
lineData = { path: [x0, y0, x1, y1, ...], strokeColor, strokeWidth }

// 站点数据:分为普通站点和换乘站点
// 普通站点绘制简单圆形
stationData = { x, y, r, fillColor, strokeColor, strokeWidth }
// 换乘站点绘制换乘图标(png图片)
stationData_transfer = { x, y, width, height }

// 线路名称
lineNameData = { text, x, y, fillColor }

// 站点名称
stationNameData = { text, x, y }
复制代码

绘图API

绘制的时候遍历绘制元素数组,根据元素类型设置上下文样式,绘制及填充。接口参考: developers.weixin.qq.com/miniprogram…

  • 设置样式:setStrokeStyle, setFillStyle, setLineWidth, setFontSize
  • 绘制路线:moveTo, lineTo, stroke
  • 绘制站点:moveTo, arc, stroke, fill
  • 绘制图片:drawImage
  • 绘制文字:fillText

交互实现

实现交互主要步骤如下:

  1. 通过 bindtouchstartbindtouchmovebindtouchend 实现对用户拖动和双指缩放的监听,得到拖动位移向量、缩放比例,触发重绘
  2. 绘制时通过 scaletranslate 在不用对数据坐标进行处理的情况下实现缩放和平移

最终得到的结果如下,平均渲染时长为 42.82 ms,真机(ios)验证:龟速移动,画面延迟非常大。

小程序Canvas性能优化实战

优化方法

完全不了解canvas优化方案的同学可以先看看:canvas的优化。

避免不必要的画布状态改变

参考 Canvas 最佳实践(性能篇) ,绘图上下文是一个状态机,状态的改变是有一定开销的。画布状态改变这里主要指 strokeStylefillStyle 等样式的改变。

如何减少这部分的开销呢?我们可以尽量让样式相同的元素放在一起进行一次性的绘制。观察一下数据可以发现,很多站点元素样式都是相同的,那么在绘制之前可以先做一次数据的聚合,将样式相同的数据组合成一条数据:

function mergeStationData(mapStation) {
  let mergedData = {}

  mapStation.forEach(station => {
    let coord = `${station.x},${station.y},${station.r}`
    let stationStyle = `${station.fillColor}|${station.strokeColor}|${station.strokeWidth}`

    if (mergedData[stationStyle]) {
      mergedData[stationStyle].push(coord)
    } else {
      mergedData[stationStyle] = [coord]
    }
  })

  return mergedData
}
复制代码

聚合后,329条站点数据合并为24条,有效的减少了90%的冗余状态改变开销。修改之后测试一下,平均渲染时长降到了 20.48 ms,真机验证:移动稍快了一些,但画面仍有较高延迟。

合并数据的时候需要注意,此应用场景下各站点是没有互相压盖的,而如果有压盖顺序的话,在合并时只能合并相邻且样式相同的数据。

减少绘制物

  1. 筛除视野外的绘制物:

    当用户在放大图像时,其实大部分绘制物都消失在了视野范围之外,避免绘制视野外的元素可以节省不必要的开销。点元素是比较容易判断是否在视野范围之外的,而站点、站点名、线路名都可以作为点元素处理;线路也可以计算出在视野范围内的部分线段,较为复杂,这里先不做处理。筛除掉视野外的绘制物之后测试一下,平均渲染时长 17.02 ms,真机验证:同上,没有太多变化。

  2. 筛除过小的绘制物:

    当用户在缩小图像时,文字和站点会由于尺寸太小而看不大清,在不影响用户体验的前提下可以考虑直接去掉。根据测试,最终决定在显示比例小于30%时去除文字和站点,这个级别下的渲染时长从 22.12 ms,减少到了 9.68 ms。

小程序Canvas性能优化实战

降低重绘频率

虽然平均渲染时长已经低了很多,但是在交互时却仍有较高的延迟,这是因为每次 ontouchmove 都会将渲染任务加入到异步队列中,事件触发频率远高于每秒能够执行的渲染次数,导致渲染任务严重积压,不断滞后。在PC端一般使用 requestAnimationFrame 解决这个问题,小程序里没有,但是可以自己实现,参考 微信小程序中使用requestAnimationFrame

const requestAnimationFrame = function (callback, lastTime) {
  var lastTime;
  if (typeof lastTime === 'undefined') {
    lastTime = 0
  }
  var currTime = new Date().getTime();
  var timeToCall = Math.max(0, 30 - (currTime - lastTime));
  lastTime = currTime + timeToCall;
  var id = setTimeout(function () {
    callback(lastTime);
  }, timeToCall);
  return id;
};

const cancelAnimationFrame = function (id) {
  clearTimeout(id);
};
复制代码

PC端我们一般将渲染间隔控制在16ms左右,但是在小程序中考虑到性能限制,且移动端各机型性能不一,所以这里留了一些空间,控制在30ms,对应到30FPS左右。

但如果一直循环调用也会造成静止状态下不必要的开销,所以可以在交互开始 ontouchstart 和结束 ontouchend 时分别开启、停止动画:

animate(lastTime) {
  this.animateId = requestAnimationFrame((t) => {
    this.render()
    this.animate(t)
  }, lastTime)
},

stop() {
  cancelAnimationFrame(this.animateId)
},
复制代码

修改之后真机验证一下:画面比较流程,有轻微卡顿,但不会延迟。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

数据挖掘概念与技术

数据挖掘概念与技术

(加)Jiawei Han;Micheline Kamber / 范明、孟小峰 / 机械工业 / 2007-3 / 55.00元

《数据挖掘概念与技术(原书第2版)》全面地讲述数据挖掘领域的重要知识和技术创新。在第1版内容相当全面的基础上,第2版展示了该领域的最新研究成果,例如挖掘流、时序和序列数据以及挖掘时间空间、多媒体、文本和Web数据。本书可作为数据挖掘和知识发现领域的教师、研究人员和开发人员的一本必读书。 《数据挖掘概念与技术(原书第2版)》第1版曾是受读者欢迎的数据挖掘专著,是一本可读性极佳的教材。第2版充实了数据......一起来看看 《数据挖掘概念与技术》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

MD5 加密
MD5 加密

MD5 加密工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具