如何将画布图像数据复制到其他变量?

时间:2017-05-27 14:43:38

标签: javascript jquery html5 canvas

我正在开发一个小应用程序,它将用户图像加载到服务器上,让他选择其中一个过滤器并返回图像。

我需要以某种方式保存初始图像数据而不应用过滤器。

但正如我发现的那样,在JS中有没有自然的方式来复制变种。

我尝试使用LoDash _.clone()和其中一个jQuery函数来执行此操作,但它们无法正常工作。

当我将克隆数据应用于图像时,函数putImageData由于类型错误而无法获取克隆数据。

似乎克隆函数以某种方式忽略了对象类型。

代码:

var img = document.getElementById("image");
var canvas = document.getElementById("imageCanvas");
var downloadLink = document.getElementById("download");
canvas.width = img.width;
canvas.height = img.height;

var context = canvas.getContext('2d');
context.drawImage(img, 0, 0, img.width, img.height);
document.getElementById("image").remove();

initialImageData = context.getImageData(0, 0, canvas.width, canvas.height); //initialImageData stores a reference to data, but I need a copy

///////////////////////
normalBtn.onclick = function(){
        if(!(currentState == converterStates.normal)){
             currentState = converterStates.normal;
             //here I need to apply cloned normal data
        }
};

那么,我能在这做什么呢?

感谢!!!

3 个答案:

答案 0 :(得分:0)

使用:

var image = …;
var data = JSON.parse(JSON.stringify(image).data);
var arr = new Uint8ClampedArray(data);
var copy = new ImageData(arr, image.width, image.height);

答案 1 :(得分:0)

ImageData对象拥有一个Uint8ClampedArray,它本身拥有ArrayBuffer

要克隆此ArrayBuffer,您可以使用其slice方法,或者您获得的TypedArray视图中的方法:



var ctx = canvas.getContext('2d');
ctx.fillStyle = 'orange';
ctx.fillRect(0,0,300,150);
var original = ctx.getImageData(0,0,300,150);

var copiedData = original.data.slice();
var copied = new ImageData(copiedData, original.width, original.height);
// now both hold the same values
console.log(original.data[25], copied.data[25]);

// but can be modified independently
copied.data[25] = 0;
console.log(original.data[25], copied.data[25]);

<canvas id="canvas"></canvas>
&#13;
&#13;
&#13;

但在您的情况下,更简单的解决方案是拨打两次ctx.getImageData

&#13;
&#13;
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'orange';
ctx.fillRect(0,0,300,150);
var original = ctx.getImageData(0,0,300,150);
var copied = ctx.getImageData(0,0,300,150);
// both hold the same values
console.log(original.data[25], copied.data[25]);

// and can be modified independently
copied.data[25] = 0;
console.log(original.data[25], copied.data[25]);
&#13;
<canvas id="canvas"></canvas>
&#13;
&#13;
&#13;

一个完整的例子:

&#13;
&#13;
var ctx = canvas.getContext('2d');
var img = new Image();
// keep these variables globally accessible to our script
var initialImageData, filterImageData;

var current = 0; // just to be able to switch easily

img.onload = function(){
  // prepare our initial state
  canvas.width = img.width/2;
  canvas.height = img.height/2;
  ctx.drawImage(img, 0,0, canvas.width, canvas.height);
  // this is the state we want to save
  initialImageData = ctx.getImageData(0,0,canvas.width,canvas.height);
  // get an other, independent, copy of the current state
  filterImageData = ctx.getImageData(0,0,canvas.width,canvas.height);
  // now we can modify one of these copies
  applyFilter(filterImageData);
  
  button.onclick = switchImageData;
  switchImageData();
  }
// remove red channel
function applyFilter(image){
  var d = image.data;
  for(var i = 0; i < d.byteLength; i+=4){
    d[i] = 0;
    }
  }
function switchImageData(){
  // use either the original one or the filtered one
  var currentImageData =  (current = +!current) ?
  filterImageData : initialImageData;
  
  ctx.putImageData(currentImageData, 0, 0);
  log.textContent = current ? 'filtered' : 'original';
  }
img.crossOrigin = 'anonymous';
img.src = 'https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg';
&#13;
<button id="button">switch imageData</button>
<code id="log"></code><br>
<canvas id="canvas"></canvas>
&#13;
&#13;
&#13;

slice相同:

&#13;
&#13;
var ctx = canvas.getContext('2d');
var img = new Image();
// keep these variables globally accessible to our script
var initialImageData, filterImageData;

var current = 0; // just to be able to switch easily

img.onload = function(){
  // prepare our initial state
  canvas.width = img.width/2;
  canvas.height = img.height/2;
  ctx.drawImage(img, 0,0, canvas.width, canvas.height);
  // this is the state we want to save
  initialImageData = ctx.getImageData(0,0,canvas.width,canvas.height);
  // get an other, independent, copy of the current state
  filterImageData = new ImageData(initialImageData.data.slice(), initialImageData.width, initialImageData.height);
  // now we can modify one of these copies
  applyFilter(filterImageData);
  
  button.onclick = switchImageData;
  switchImageData();
  }
// remove red channel
function applyFilter(image){
  var d = image.data;
  for(var i = 0; i < d.byteLength; i+=4){
    d[i] = 0;
    }
  }
function switchImageData(){
  // use either the original one or the filtered one
  var currentImageData =  (current = +!current) ?
  filterImageData : initialImageData;
  
  ctx.putImageData(currentImageData, 0, 0);
  log.textContent = current ? 'filtered' : 'original';
  }
img.crossOrigin = 'anonymous';
img.src = 'https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg';
&#13;
<button id="button">switch imageData</button>
<code id="log"></code><br>
<canvas id="canvas"></canvas>
&#13;
&#13;
&#13;

答案 2 :(得分:0)

复制类型化数组的正确方法是通过静态函数from

例如

var imageData = ctx.getImageData(0,0,100,100);
var copyOfData = Uint8ClampedArray.from(imageData.data); // create a Uint8ClampedArray copy of imageData.data

它还允许您转换类型

var copyAs16Bit = Uint16Array.from(imageData.data); // Adds high byte. 0xff becomes 0x00ff

请注意,转换为较小的类型时,会为整数截断额外的位。从浮点数转换时,不会复制值而不是位。在有符号和无符号整数之间进行复制时,会复制这些位,例如Uint8ArrayInt8Array会将255转换为-1。当从小int转换为较大的uint时,例如Int8ArrayUint32Array将添加位-1变为0xffff

您还可以添加可选的地图功能

// make a copy with aplha set to half.
var copyTrans = Uint8ClampedArray.from(imageData.data, (d, i) => i % 4 === 3 ? d >> 1 : d);

typedArray.from将创建任何数组或可迭代对象的副本。