为什么我在不经意间使用`CanvasRenderingContext2D.drawImage()`将图像切割成'tiles'来缩放图像?

时间:2016-05-13 14:26:22

标签: javascript ecmascript-6 drawimage

由于没有特别的原因,除了不熟悉canvasCanvasRenderingContext2D.drawImage()之外,我尝试编写一个函数,该函数将采用<image>元素和其他选项,例如作为<image>应该被'切片'的'行'和'cols'的数量。

目前,作为第一步,应将“切片”图片部分转移到<canvas>附加的已创建document元素中。

虽然这个功能有效但是我没有改变默认的行数和列数 - 但结果却因某种原因被裁剪,例如这个源图像:

缩放到这个:

我完全接受这可能是我的数学应用中的混乱,并且几乎肯定必须在CanvasRenderingContext2D.drawImage()范围内,因此可能位于for循环中但是已经盯着它,并且喝了几杯咖啡,我还没看到我的傻瓜在哪里。

我的代码片段:

// Using ES6/ECMAScript 2015 'let' operator

function imageFragment(opts) {
  // defaults for the function (to be overridden)
  // by user supplied values (if any are passed):
  let settings = {
    'source': document.querySelector('img'),
    'rows': 2,
    'cols': 2,
    'trim': true
  };

  // Using Object.keys to retrieve the keys of the
  // opts Object, or an empty object (without keys)
  // should no opts Object be supplied;
  // using Array.prototype.forEach() to iterate over
  // the Array of keys, using Arrow syntax, to
  // update the defaults settings to the user-defined
  // settings:
  Object.keys(opts || {}).forEach(key => settings[key] = opts[key]);

  let image = settings.source,
    frame = document.createElement('div'),
    canvas = document.createElement('canvas'),
    imageHeight = image.clientHeight,
    imageWidth = image.clientWidth,
    tileHeight = parseInt(imageHeight / settings.rows, 10),
    tileWidth = parseInt(imageWidth / settings.cols, 10),
    // following two variables, heightRemainder and
    // widthRemainder, are not currently used, but here
    // in anticipation of settings.trim being set to 'false':
    heightRemainder = imageHeight % tileHeight,
    widthRemainder = imageWidth % tileWidth,
    nCols = settings.cols + (settings.trim === true ? 0 : 1),
    nRows = settings.rows + (settings.trim === true ? 0 : 1),
    context = canvas.getContext('2d');

  // updating properties of the frame and canvas
  // element nodes:
  frame.classList.add('canvasWrapper');
  canvas.style.width = nCols * tileWidth + 'px';
  canvas.style.height = nRows * tileHeight + 'px';
  canvas.classList.add('tile');

  // using nested for loops to iterate over each of
  // the 'columns' within each 'row' (I strongly
  // believe this could be revised, and hopefully
  // simplified):
  for (let r = 0; r < nRows; r++) {
    for (let c = 0; c < nCols; c++) {

      // defining the section of the image to copy,
      // image: the source image,
      // (r * tileWidth): the x-offset position of
      // the upper-left corner of the 'segment' to
      // copy on this iteration,
      // (c * tileHeight): the y-offset position of
      // the upper-left corner of the 'segment',
      // tileWidth: the width of the 'segment' to copy,
      // tilleHeight: the height of the 'segment' to copy,
      // (r * tileWidth): the target horizontal position of the
      // upper-left corner of the copied 'segment' in
      // the target (the canvas),
      // (c * tileHeight): the target vertical position of
      // the upper-left corner of the copied 'segment' in the
      // target,
      // tileWidth and tileHeight: the width and height of
      // the copied segment in the target:
      context.drawImage(image, (r * tileWidth), (c * tileHeight), tileWidth, tileHeight, (r * tileWidth), (c * tileHeight), tileWidth, tileHeight);
    }
  }

  // appending the canvas to the frame:
  frame.appendChild(canvas);
  // inserting the frame ahead of the image within
  // the image's parentNode:
  image.parentNode.insertBefore(frame, image);
}

imageFragment({
  'rows': 3
});
<img src="https://i.imgur.com/iWKad22m.jpg" />

JS Fiddle demo

并且,在猜测之前,经典的脸型是专门为这个问题选择的,但确切地复制了“原始”源图像的问题。

1 个答案:

答案 0 :(得分:0)

您应该设置画布大小。目前,您只使用CSS调整默认的300x150大小。

canvas.width = imageWidth;
canvas.height = imageHeight;

然后你不应该强迫{rows: 3}

// Using ES6/ECMAScript 2015 'let' operator

function imageFragment(opts) {
  // defaults for the function (to be overridden)
  // by user supplied values (if any are passed):
  let settings = {
    'source': document.querySelector('img'),
    'rows': 2,
    'cols': 2,
    'trim': true
  };

  // Using Object.keys to retrieve the keys of the
  // opts Object, or an empty object (without keys)
  // should no opts Object be supplied;
  // using Array.prototype.forEach() to iterate over
  // the Array of keys, using Arrow syntax, to
  // update the defaults settings to the user-defined
  // settings:
  Object.keys(opts || {}).forEach(key => settings[key] = opts[key]);

  let image = settings.source,
    frame = document.createElement('div'),
    canvas = document.createElement('canvas'),
    imageHeight = image.clientHeight,
    imageWidth = image.clientWidth,
    tileHeight = parseInt(imageHeight / settings.rows, 10),
    tileWidth = parseInt(imageWidth / settings.cols, 10),
    // following two variables, heightRemainder and
    // widthRemainder, are not currently used, but here
    // in anticipation of settings.trim being set to 'false':
    heightRemainder = imageHeight % tileHeight,
    widthRemainder = imageWidth % tileWidth,
    nCols = settings.cols + (settings.trim === true ? 0 : 1),
    nRows = settings.rows + (settings.trim === true ? 0 : 1),
    context = canvas.getContext('2d');

  // updating properties of the frame and canvas
  // element nodes:
  frame.classList.add('canvasWrapper');
  canvas.width = imageWidth;
  canvas.height = imageHeight;
  canvas.style.width = nCols * tileWidth + 'px';
  canvas.style.height = nRows * tileHeight + 'px';
  canvas.classList.add('tile');

  // using nested for loops to iterate over each of
  // the 'columns' within each 'row' (I strongly
  // believe this could be revised, and hopefully
  // simplified):
  for (let r = 0; r < nRows; r++) {
    for (let c = 0; c < nCols; c++) {
      
      // defining the section of the image to copy,
      // image: the source image,
      // (r * tileWidth): the x-offset position of
      // the upper-left corner of the 'segment' to
      // copy on this iteration,
      // (c * tileHeight): the y-offset position of
      // the upper-left corner of the 'segment',
      // tileWidth: the width of the 'segment' to copy,
      // tilleHeight: the height of the 'segment' to copy,
      // (r * tileWidth): the target horizontal position of the
      // upper-left corner of the copied 'segment' in
      // the target (the canvas),
      // (c * tileHeight): the target vertical position of
      // the upper-left corner of the copied 'segment' in the
      // target,
      // tileWidth and tileHeight: the width and height of
      // the copied segment in the target:
      context.drawImage(image, (r * tileWidth), (c * tileHeight), tileWidth, tileHeight, (r * tileWidth), (c * tileHeight), tileWidth, tileHeight);
    }
  }

  // appending the canvas to the frame:
  frame.appendChild(canvas);
  // inserting the frame ahead of the image within
  // the image's parentNode:
  image.parentNode.insertBefore(frame, image);
}

imageFragment({
  // 'rows' : 3
});
<img src="https://i.imgur.com/iWKad22m.jpg" />