【问题标题】:How to get image from responsive canvas?如何从响应式画布中获取图像?
【发布时间】:2015-12-01 09:49:54
【问题描述】:

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

这是画布的一部分,我正在根据用户操作绘制图片和项目,它可以工作。

<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>
<button id="btn_guardar">Guardar</button>

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

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;
});

现在我必须实现这个响应式,但重新调整画布大小,最终图像也会发生变化。如何从较小的画布上获取 1200x630 的图像?

阐明用户可以通过拖放重新定位画布内的元素。

您好!

【问题讨论】:

    标签: javascript html canvas


    【解决方案1】:

    您可以使用 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>

    【讨论】:

    • 太好了,对我帮助很大!
    【解决方案2】:

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

    您的服务器上的数据会小得多,而您的用户将能够再次修改显示。

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

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

    (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();
    })();
    canvas {
      border: 1px solid
    }
    <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>

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

    【讨论】:

      猜你喜欢
      • 2020-08-04
      • 2021-07-16
      • 1970-01-01
      • 1970-01-01
      • 2021-09-15
      • 1970-01-01
      • 2015-10-12
      • 2019-10-04
      • 2015-12-30
      相关资源
      最近更新 更多