首页 > 解决方案 > HTML5 Canvas 绘制一堆可调整大小图形的最佳方式

问题描述

我目前正在为我的网站创建一些艺术背景,其中包含一堆随机生成的曲线图。

演示

到目前为止,我使用的是 Html5 Canvas,每次调整窗口大小以适应画布的新宽度时,我都会重新创建整个画面。数据都位于 x,y ~ [0,1],[0,1] 中,然后缩放以适合窗口。这就是我设置画布转换的方式:

ctx.resetTransform();
ctx.scale(c.width, c.height);

由于基本数据没有改变,我希望我可以调整画布大小(删除所有数据)和更新绘图的转换(尽管我想我可能会在笔触宽度方面遇到麻烦,当抚摸线条时[0,1] 空间)。有没有更有效的方法让我重新绘制所有这些图(例如以某种方式存储所有线路径),或者我真的必须再次遍历所有内容并重新创建整个场景?

标签: javascriptoptimizationplotgraphhtml5-canvas

解决方案


Path2D

用于 Path2D创建内容一次,然后根据需要按比例渲染。

例如

const pathStroke = new Path2D(), pathFill = new Path2D();
var i;
for (i = 0; i <= 1; i += 1 / 100) {
    pathStroke.lineTo(i, Math.sin(i * Math.PI * 8) * 0.1 + 0.2);
    pathFill.lineTo(i, Math.sin(i * Math.PI * 16) * 0.1 + 0.4);
}
for (i = 0; i <= 1; i += 1 / 100) {
    pathFill.lineTo((1-i), Math.cos((i-i) * Math.PI * 13) * 0.1 + 0.5);
}

然后渲染内容

ctx = canvas.getContext("2d");
ctx.setTransform(canvas.width, 0, 0, canvas.height, 0, 0);
ctx.stroke(pathStroke);
ctx.fill(pathFill);

演示

切换到整页以查看重新渲染和缩放的内容。

  • 该函数createPaths将内容添加到两个路径pathStrokepathFill
  • 该函数renderContent将路径中的内容呈现为适合画布的缩放。resize 事件只是再次调用这个函数。

const ctx = canvas.getContext("2d");
const pathStroke = new Path2D(), pathFill = new Path2D();
createPaths();
renderContent();
addEventListener("resize", renderContent);

function createPaths() {
  var i;
  for (i = 0; i <= 1; i += 1 / 1000) {
      pathStroke.lineTo(i * i, Math.sin(i * Math.PI * 8) * 0.05 + 0.1);
      pathFill.lineTo(i, Math.sin(i * Math.PI * 11) * 0.4 * i ** 0.5  + 0.6);
  }
  for (i = 0; i <= 1; i += 1 / 1000) {
      if (i === 0) {
          pathStroke.moveTo((1-i), Math.sin(i * i * Math.PI * 8) * (i * 0.1) + 0.2);      
      } else {
          pathStroke.lineTo((1-i), Math.sin(i * i * Math.PI * 9) * (i * 0.1) + 0.2);       
      }
      pathFill.lineTo((1-i), Math.cos((1-i) * Math.PI * 13) * 0.3 * i * i + 0.7);
  }
}


function renderContent() {
    canvas.width = innerWidth;
    canvas.height= innerHeight;
    
    // scale line width 2 pixels
    ctx.lineWidth = 2 / Math.max(canvas.width, canvas.height);

    ctx.setTransform(canvas.width, 0, 0, canvas.height, 0, 0);
    ctx.stroke(pathStroke);
    ctx.fill(pathFill);
}


    
body { padding: 0px }
canvas {
   position: absolute;
   top: 0px;
   left: 0px;
}
<canvas id="canvas"></canvas>


推荐阅读