使用 drawImage 裁剪在 Safari 中不起作用 [英] Cropping with drawImage not working in Safari

查看:22
本文介绍了使用 drawImage 裁剪在 Safari 中不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用画布处理一些简单的图像处理功能.用户上传图像,能够旋转和裁剪它,然后单击确定.然后将图像分成两半,每一半绘制镜像到两个画布元素,如下所示:

I'm working on some simple image manipulation functions with canvas. The user uploads an image, is able to rotate and crop it and then clicks ok. The image is then split in half with each half drawn mirrored to two canvas elements, like this:

原文

镜像

这一切在 Chrome、Firefox、IE 和 Android 设备上都运行良好.Safari 不会很好玩.除了拆分功能外,所有图像处理都可以正常工作.它确实绘制到画布元素之一,但另一个只是黑色.我试过更改 drawImage 代码,但我无法让它工作.

It all works great in Chrome, Firefox, IE and Android devices. Safari won't play nice though. All the image manipulation works fine except the split function. It does draw to one of the canvas elements, but the other is just black. I've tried changing the drawImage code around, but I just can't get it to work.

功能如下:

function splitImage(canvas, context, image, isLeftSide) {
  canvas.width = img.width;
  canvas.height = img.height;
  context.save();
  if(isLeftSide) {
    context.drawImage(
      image, 
      image.width / 2,
      0, 
      image.width, 
      image.height, 
      canvas.width / 2, 
      0, 
      canvas.width, 
      canvas.height
    );
    context.scale(-1, 1);
    context.drawImage(
      image, 
      image.width / 2, 
      0, 
      image.width, 
      image.height, 
      -canvas.width / 2, 
      0, 
      canvas.width, 
      canvas.height
    );
  } else {
    context.drawImage(
      image, 
      0, 
      0, 
      image.width / 2, 
      image.height, 
      0, 
      0, 
      canvas.width / 2, 
      canvas.height
    );
    context.scale(-1, 1);
    context.drawImage(
      image, 
      0, 
      0, 
      image.width / 2, 
      image.height, 
      -canvas.width, 
      0, 
      canvas.width / 2, 
      canvas.height
    );
  }
  context.restore();
  download(canvas);
}

确切地说,是 if(isLeftSide) 中的 drawImage 操作在 Safari 中不起作用.

To be exact, it's the drawImage operations inside the if(isLeftSide) that doesn't work in Safari.

有什么想法吗?

它似乎也不适用于 iOS 设备.我了解到 Safari 和 iOS 设备在处理大图像时可能会耗尽内存.为了抵消这一点(并减少一些滞后),我添加了一个调整大小功能.如有必要,图像将调整为最大 800 px 宽和 800 px 高,保持纵横比不变.这是在任何其他图像处理之前完成的,但没有任何区别.

It doesn't seem to work on iOS devices either. I've read that Safari and iOS devices might run out of memory when working with large images. To counteract this (and reduce some lag) I've added a resize function. The image is resized to a maximum of 800 px width and 800 px height if necessary, keeping the aspect ratio intact. This is done before any other image manipulation, but hasn't made any difference.

调整大小功能:

function resizeImage() {
  var size = 800;
  if(imgTemp.width > size && imgTemp.width >= imgTemp.height) {
    imgTemp.height = (imgTemp.height / imgTemp.width) * size;
    imgTemp.width = size;
  } else if (imgTemp.height > size && imgTemp.height > imgTemp.width) {
    imgTemp.width = (imgTemp.width / imgTemp.height) * size;
    imgTemp.height = size;
  }
}

推荐答案

drawImage() 在 sourceImage 的边界外被调用时出现的错误.

The bug occurs when drawImage() is called out of the bounds of the sourceImage.

您必须仔细检查源宽度和源高度是否始终小于或等于图像的宽度和高度:

You have to double check that the source width and source height are always smaller or equal to the image's width and height :

所以对于第一个 if 块:

So for the first if block :

var sourceX = image.width/2;
var sourceY = 0;
var sourceWidth = image.width - sourceX; // you're in the bounds
var sourceHeight = image.height;
var destX = canvas.width/2;
var destY = 0;
var destWidth = canvas.width;
var destHeight = canvas.height;

ctx.drawImage(image, sourceX, sourceY, sourceWidth, sourceHeight, destX, destY, destWidth, destHeight);

或者作为单线:

ctx.drawImage(image, image.width/2, 0, image.width - (image.width/2), image.height, canvas.width/2, 0, canvas.width, canvas.height);


Ps:对于 最近的项目,我必须对这个 Safari 错误进行完整的猴子补丁.你可以在这个要点和下面的代码片段中找到它:


Ps: For a recent project I had to make a complete monkey-patch over this Safari bug. You can find it in this gist, and in below code-snippet:

const canvas = document.getElementById( "canvas" );
const ctx = canvas.getContext( "2d" );
ctx.fillRect( 0, 0, 80, 80 );
ctx.drawImage( canvas, -100, -100, 180, 180, 30, 30, 90, 90 );

<canvas id="canvas" width="300" height="300"></canvas>
<script>
// drawImage monkey-patch for Safari
(()=> {

  if( !needPoly() ) { return; }

  const proto = CanvasRenderingContext2D.prototype;
  const original = proto.drawImage;
  if( !original ) {
    console.error( "This script requires a basic implementation of drawImage" );
    return;
  }

  proto.drawImage = function drawImage( source, x, y ) { // length: 3

    const will_crop = arguments.length === 9;
    if( !will_crop ) {
      return original.apply( this, [...arguments] );
    }

    const safe_rect = getSafeRect( ...arguments );
    if( isEmptyRect( safe_rect ) ) {
      return;
    }
    return original.apply( this, safe_rect );
  } 

  function needPoly() {
    const ctx = document.createElement( "canvas" ).getContext( "2d" );
    ctx.fillRect( 0, 0, 40, 40 );
    ctx.drawImage( ctx.canvas, -40, -40, 80, 80, 50, 50, 20, 20 );

    const img = ctx.getImageData( 50, 50, 30, 30 ); // 10px around expected square
    const data = new Uint32Array( img.data.buffer );
    const colorAt = (x, y) => data[ y * img.width + x ];

    const transparents = [ [ 9, 9 ], [ 20, 9 ], [ 9, 20 ], [ 20, 20 ] ];
    const blacks = [ [ 10, 10 ], [ 19, 10 ], [ 10, 19 ], [ 19, 19 ] ];
    return transparents.some( ([ x, y ]) => colorAt( x, y ) !== 0x00000000 ) ||
      blacks.some( ([ x, y ]) => colorAt( x, y ) === 0x00000000 )
  }

  function getSafeRect( image, sx, sy, sw, sh, dx, dy, dw, dh ) {
  
    const { width, height } = getSourceDimensions( image );
    
    if( sw < 0 ) {
      sx += sw;
      sw = Math.abs( sw );
    }
    if( sh < 0 ) {
      sy += sh;
      sh = Math.abs( sh );
    }
    if( dw < 0 ) {
      dx += dw;
      dw = Math.abs( dw );
    }
    if( dh < 0 ) {
      dy += dh;
      dh = Math.abs( dh );
    }
    const x1 = Math.max( sx, 0 );
    const x2 = Math.min( sx + sw, width );
    const y1 = Math.max( sy, 0 );
    const y2 = Math.min( sy + sh, height );
    const w_ratio = dw / sw;
    const h_ratio = dh / sh;

    return [
      image,
      x1,
      y1,
      x2 - x1,
      y2 - y1,
      sx < 0 ? dx - (sx * w_ratio) : dx,
      sy < 0 ? dy - (sy * h_ratio) : dy,
      (x2 - x1) * w_ratio,
      (y2 - y1) * h_ratio
    ];

  }

  function isEmptyRect( args ) {
    // sw, sh, dw, dh
    return [ 3, 4, 7, 8 ].some( (index) => !args[ index ] );
  }

  function getSourceDimensions( source ) {
    const sourceIs = ( type ) => {
      const constructor = globalThis[ type ];
      return constructor && (source instanceof constructor);
    };
    if( sourceIs( "HTMLImageElement" ) ) {
      return { width: source.naturalWidth, height: source.naturalHeight };
    }
    else if( sourceIs( "HTMLVideoElement" ) ) {
      return { width: source.videoWidth, height: source.videoHeight };
    }
    else if( sourceIs( "SVGImageElement" ) ) {
      throw new TypeError( "SVGImageElement isn't yet supported as source image.", "UnsupportedError" );
    }
    else if( sourceIs( "HTMLCanvasElement" ) || sourceIs( "ImageBitmap" ) ) {
      return source;
    }
  }

})();

</script>

这篇关于使用 drawImage 裁剪在 Safari 中不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆