如何从响应式画布获取图像?

时间:2015-12-01 09:49:55

标签: javascript html5 canvas

我需要从2个画布生成1200x630的最终图像,不一定要具有该尺寸,例如从移动设备查看应用程序。

这是画布的一部分,我根据用户操作绘制图片和项目,它有效。

var canvas_marcos = document.getElementById("canvas-marco");
var ctx_marcos = canvas_marcos.getContext("2d");
var canvas_foto = document.getElementById("canvas-foto");
var ctx_foto = canvas_foto.getContext("2d");
var canvas_wrapper = document.getElementById("canvas-wrapper");
var ctx_wrapper = canvas_wrapper.getContext("2d");

$('#btn_guardar').click(function(){
    ctx_wrapper.drawImage(canvas_foto, 0, 0);
    ctx_wrapper.drawImage(canvas_marcos, 0, 0);

    var dataURL = canvas_wrapper.toDataURL();

    var img = new Image();

    img.onload = function(){
        document.body.appendChild(img);
    }
    img.src = dataURL;
});

这将是负责生成图像的JS的一部分,例如,只是在屏幕上松动,尽管我将其保留在服务器上。

{{1}}

现在我必须实现这种响应,但重新调整画布大小,最终图像也会改变。如何从较小的画布获得1200x630的图像?

说明用户可以通过拖放操作重新定位画布内的元素。下降。

问候!

2 个答案:

答案 0 :(得分:0)

您可以使用CSS更改画布显示大小。此示例将宽度设置为100%缩小或增大图像而不更改画布。

var canvas_marcos = document.getElementById("canvas-marco");
var ctx_marcos = canvas_marcos.getContext("2d");
var canvas_foto = document.getElementById("canvas-foto");
var ctx_foto = canvas_foto.getContext("2d");
var canvas_wrapper = document.getElementById("canvas-wrapper");
var ctx_wrapper = canvas_wrapper.getContext("2d");

$('#btn_guardar').click(function() {
  ctx_wrapper.drawImage(canvas_foto, 0, 0);
  ctx_wrapper.drawImage(canvas_marcos, 0, 0);

  var dataURL = canvas_wrapper.toDataURL();

  var img = new Image();
  img.style.position = "absolute";
  img.style.left = "0";
  img.style.top = "0";
  img.style.width = "100%";

  img.onload = function() {
    document.body.appendChild(img);
    this.addEventListener("click", function() {
      this.parentElement.removeChild(this);
      reset();
    });
  }

  img.src = dataURL;
});

// added by me

function toggleCanvas() {
  $(this).toggleClass('off');
  var can_id = "canvas-" + this.id.replace("btn_", "");
  $('#' + can_id).toggleClass('hide');
}

$('#btn_foto').click(toggleCanvas);
$('#btn_marco').click(toggleCanvas);
$('#btn_wrapper').click(toggleCanvas);


function clear(ctx, color, text, y_offset) {
  ctx.fillStyle = color;
  var w = ctx.canvas.width;
  var h = ctx.canvas.height;
  ctx.clearRect(0,0,w,h);
  ctx.fillRect(0, 0, w, h);
  ctx.fillStyle = "white";
  ctx.font = "50px sans-serif";
  ctx.textAlign = "center";
  ctx.textBaseline = "middle";
  ctx.fillText(text, w / 2, h / 2 + y_offset);
}

function reset() {
  clear(ctx_foto, "rgba(0,0,0,.7)", "canvas-foto", 0);
  clear(ctx_marcos, "rgba(0,100,0,.7)", "canvas-marco", 50);
  clear(ctx_wrapper, "rgba(10,30,130,.7)", "canvas-wrapper", 100);
}

reset();
body {
  padding: 0;
  margin: 0;
}
#canvas-foto,
#canvas-marco,
#canvas-wrapper {
  width: 100%;
  margin: 0;
  position: absolute;
  left: 0;
  top: 0;
}
#controls {
  position: absolute;
  background-color: #00CC66;
  padding: 10px;
}
.hide {
  display: none;
}
button.off {
  background-color: lightgray;
  color: gray;
  border-color: gray;
}
button.off span {
  visibility: hidden;
}
button {
  background-color: white;
  color: black;
  border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
  <canvas width="1200" height="630" id="canvas-foto">Tu navegador no soporta esta aplicaci&oacute;n</canvas>
  <canvas width="1200" height="630" id="canvas-marco">Tu navegador no soporta esta aplicaci&oacute;n</canvas>
  <canvas width="1200" height="630" id="canvas-wrapper">Tu navegador no soporta esta aplicaci&oacute;n</canvas>
</div>
<div id="controls">
  <button id="btn_guardar">Guardar</button>
  <button id="btn_foto">foto <span>&#10003;</span>
  </button>
  <button id="btn_marco">marco <span>&#10003;</span>
  </button>
  <button id="btn_wrapper">wrapper <span>&#10003;</span>
  </button>
</div>

答案 1 :(得分:0)

如果您需要在服务器上存储的是用户在画布上移动不同对象后的最终图像,则最佳解决方案IMO将对象位置存储为JSON字符串并重绘它而不是保存渲染图像。

您的服务器上的数据会小得多,您的用户也可以再次修改显示。

这样,您就可以在每个屏幕尺寸上正确地重绘它,避免CSS抖动,甚至可以以任何你想要的尺寸导出。

通过使用ctx.scale(),每个绘图操作甚至可以为任何比例保持相同的位置,您只需要为鼠标事件计算它。

&#13;
&#13;
(function() {
  var ctx = canvas.getContext('2d');


  ////////////////////
  // Object Storage //
  ////////////////////

  // unfortunately, localStorage on stack-snippet  will throw a security exception ...
  /*
  // get saved from localStorage, could be from server as well
  var fromStorage = function(){
    var str = localStorage.getItem('objects');
  	if(!str) return false;
  	return JSON.parse(str);
  	};
  		
  // save to localStorage or server
  var toStorage = function(){
  	localStorage.setItem('objects', JSON.stringify(objects));
  	}
  */
  // ... so just show the string that could be saved on server
  var toStorage = function() {
    log.innerHTML = JSON.stringify(objects);
  };

  save.onclick = toStorage;

  // initiate the objects previously stored or default one

  // var objects = fromStorage() || [{...}] Once again won't work in stack-snippets
  var objects = [
    {type:'rect', x:900, y:360, width:100, height:100, color:'blue'},
    {type:'circle',  x:150, y:60, width:72, height:72, color:'red'},
    {type:'image', url:"http://lorempixel.com/500/500", x:500, y:250, width: 300, height:300}
  ];

  // initialise our canvas
  var init = function() {
    // set the canvas' size and get the ratio
    setSize(sizes[0]);

    // check if there are image to load
    for (var i = 0; i < objects.length; i++) {
      if (objects[i].type === 'image') {
        imagesToLoad++;
        loadImage(objects[i]);
      }
    }
    // if there's none, draw already
    if (!imagesToLoad) draw();
  };

  // we only pass the desired width since we'll get the height from the ratio
  var setSize = function(width) {
    // set the canvas width
    canvas.width = width;
    // get the ratio of the current width divided by the desired one
    // store it in the canvas element so we can use it after in mouse events
    var ratio = canvas.ratio = canvas.width / 1200;
    // set our canvas height
    canvas.height = ratio * 630;
    // set the context scale to the ratio so we don't care about it while drawing
    ctx.scale(ratio, ratio);
  }

  // the drawing part
  var draw = function() {
    // clear the canvas at the default size since we changed the context's scale
    ctx.clearRect(0, 0, 1200, 630);
    // iterate through our objects array
    for (var i = 0; i < objects.length; i++) {
      var obj = objects[i];
      // set the current color to the one of the object
      ctx.fillStyle = obj.color;
      // do different drawing operations based on object's type
      switch (obj.type) {
        case 'rect':
          ctx.fillRect(obj.x, obj.y, obj.width, obj.height);
          break;
        case 'circle':
          ctx.beginPath();
          ctx.arc(obj.x + obj.width / 2, obj.y + obj.width / 2, obj.width / 2, Math.PI * 2, 0);
          ctx.fill();
          break;
        case 'image':
          ctx.drawImage(obj.img, obj.x, obj.y, obj.width, obj.height);
          break;
      }
    }
  };


  var imagesToLoad = 0;
  // a function to load all our image resources
  var loadImage = function(obj) {
    var img = new Image();
    img.onload = function() {
      obj.img = this;
      if (!--imagesToLoad)
        draw();
    }
    img.src = obj.url;
  };

  //////////////////
  // mouse Events //
  //////////////////

  var activeObjects;
  var mousedownHandler = function(e) {
    var rect = canvas.getBoundingClientRect();
    var x = (e.clientX - rect.left) / canvas.ratio;
    var y = (e.clientY - rect.top) / canvas.ratio;
    activeObjects = getActiveObjects(x, y);
  };

  var mouseupHandler = function(e) {
    // we stopped dragging, reset the activeObjects array
    activeObjects = null;
  }

  var mousemoveHandler = function(e) {
    // if there is no object being dragged
    if (!activeObjects) return;
    // x and y coordinates have to be scaled by our actual ratio
    var rect = canvas.getBoundingClientRect();
    var x = (e.clientX - rect.left) / canvas.ratio;
    var y = (e.clientY - rect.top) / canvas.ratio;
    // iterate through all active objects
    for (var i = 0; i < activeObjects.length; i++) {
      // and update their position
      update(activeObjects[i], x, y);
    }
  };

  var getActiveObjects = function(x, y) {
    var arr = [];
    for (var i = 0; i < objects.length; i++) {
      var obj = objects[i];
      if (obj.x < x && obj.x + obj.width > x && obj.y < y && obj.y + obj.height > y)
        arr.push(obj);
    }
    if (!arr.length) return null;
    else return arr;
  };

  var update = function(obj, x, y) {
    obj.x = x - (obj.width / 2);
    obj.y = y - (obj.height / 2);
    draw();
  }

  canvas.addEventListener('mousedown', mousedownHandler);
  canvas.addEventListener('mouseup', mouseupHandler);
  canvas.addEventListener('mousemove', mousemoveHandler);


  /////////////////////////////
  // Button events (for demo) //
  /////////////////////////////

  // store different sizes for our canvas
  var sizes = [300, 200, 600, 3000];
  var currentSize = 0;
  resize.onclick = function() {
    // iterate through the sizes array
    setSize(sizes[++currentSize % sizes.length]);
    draw();
  };

  // randomise our objects shapes and position	
  rand.onclick = function() {
      for (var i = 0; i < objects.length; i++) {
        var obj = objects[i];
        obj.x = Math.random() * 1000;
        obj.y = Math.random() * 500;
        obj.width = Math.random() * 500;
        obj.height = (obj.type === 'circle') ? obj.width : Math.random() * 500;
      }
      draw();
    }
    // Let's go !
  init();
})();
&#13;
canvas {
  border: 1px solid
}
&#13;
<button id="resize">resize canvas</button>
<button id="save">save</button>
<button id="rand">randomize</button><br>
<canvas id="canvas"></canvas>
<br><p id="log"></p>
&#13;
&#13;
&#13;

Here is a fiddle保存到localStorage也可以。