调用 drawImage 绘制图片没有效果

  • 现象:在代码中绘制图片没有效果,但是在控制台直接写绘制相关的代码却可以绘制出来

MDN 上关于 drawImage 的说明
void ctx.drawImage(image, dx, dy);
void ctx.drawImage(image, dx, dy, dWidth, dHeight);
void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);

参数说明

除了参数外,还需要确保调用 drawImage 时图片已加载完毕,即确保图片 onload 后才进行绘制,比如 imgDom.onload = function() { 绘制相关代码 }

// 使用 Promise 的方法
let loadImage = function (imgUrl, width, height) {
  let sampleImg = new Image(width, height);
  sampleImg.src = imgUrl;
  // 等待图片加载完成
  return new Promise((res) => {
    sampleImg.onload = () => {
      res(sampleImg );
    }
  });
}

图片绘制到画布后发生拉伸、变形

  • 现象:已经通过 css 将画布的宽高调整为图像合适的数值,但是绘制出的图片出现拉伸

  • 画布的 width、height 属性要与 css 属性里设置的一致,需要直接在 canvas 标签中指定宽高

图片拼接时存在缝隙

  • 现象:使用雪碧图在画布上进行拼接时,发现各个图片中间存在较小的缝隙

  • 使用绘图函数时,如果传入的参数时浮点数,可能因浏览器的抗锯齿处理导致问题

以下片段摘自:MDN

避免浮点数的坐标点,用整数取而代之
当你画一个没有整数坐标点的对象时会发生子像素渲染。

ctx.drawImage(myImage, 0.3, 0.5);

浏览器为了达到抗锯齿的效果会做额外的运算。为了避免这种情况,请保证在你调用drawImage()函数时,用Math.floor()函数对所有的坐标点取整。

在 Canvas 上进行缩放、颜色转换时失真

  • 实现方案:这里使用了一个颜色映射对将要在画布内进行绘制的图片进行颜色转换,通知进行缩放操作。
  • 现象:多个颜色的边界出现一些未能变色的色带
  • 原因:在进行同时进行缩放和变色时,原图因缩放导致部分像素颜色发生改变,因而不满足映射关系导致变色失败
  • 解决方案:创建虚拟画布,仅用于变色,完成变色后,使用中转画布作为图像,绘制到结果的画布上,此时进行缩放并不会存在这个问题,这里给出实现代码
/**
 * 在画布指定位置绘制颜色转换后的图像
 * @param {CanvasImageSource} img 原图像
 * @param {number} x 原图坐标
 * @param {number} y 原图坐标
 * @param {number} wid 原图截取宽度
 * @param {number} hei 原图截取高度
 * @param {number} targetX 目标坐标
 * @param {number} targetY 目标坐标
 * @param {string} targetColor 需要转换成的颜色,合法值为:red, green, dark, purple, yellow, bw
 */
let drawColorChangedImg = function (img, x, y, wid, hei, targetX, targetY, targetColor) {
  // 创建虚拟画布
  let vcanvas = $('<canvas id="vcanvas" width="100", height="100"></canvas>');
  let vctx = vcanvas[0].getContext('2d');
  vctx.clearRect(0, 0, 100, 100);
  // 绘制到虚拟画布
  vctx.drawImage(img, x, y, wid, hei, 0, 0, wid, hei);
  let sourceData = vctx.getImageData(0, 0, wid, hei); // 获取虚拟画布上的图像数据
  let dat = sourceData.data;
  // 转换颜色
  for (let i = 0; i < dat.length; i += 4) {
    // colorMap 是外部的一个颜色映射
    for (let j = 0; j < colorMap.blue.length; j++) {
      let rgbBlue = colorMap.blue[j];
      if (targetColor == 'bw') { // 黑白处理
        let midVal = ~~((dat[i] + dat[i + 1] + dat[i + 2]) / 3);
        dat[i] = dat[i + 1] = dat[i + 2] = midVal;
      } else if (dat[i] === rgbBlue[0] &&
        dat[i + 1] === rgbBlue[1] &&
        dat[i + 2] === rgbBlue[2]) {
        dat[i] = colorMap[targetColor][j][0];
        dat[i + 1] = colorMap[targetColor][j][1];
        dat[i + 2] = colorMap[targetColor][j][2];
      }
    }
  }
  vctx.putImageData(sourceData, 0, 0, 0, 0, wid, hei); // 绘制回虚拟画布
  // 缩放绘制,scale 为外部的缩放参数
  ctx.drawImage(vcanvas[0], 0, 0, wid, hei, targetX, targetY, wid * scale + 1, hei * scale + 1);
}