Unable to make a chunk from canvas

感情迁移 提交于 2019-12-24 07:40:07

问题


I want to have such an interaction with a map, that when a user selects some area on the map, an image is created from this area. I made a minimized and compiltely reproducible example in this fiddle. The most important part is this crop function:

function crop(img, sx, sy, dw, dh) {
   var canvas = document.createElement('canvas');
   var ctx = canvas.getContext('2d');
   canvas.width = dw;
   canvas.height = dh;
   ctx.drawImage(img, sx, sy, dw, dh, 0, 0, dw, dh);
   return canvas.toDataURL("image/png");
}

This function creates a chunk out of selected area. This is how selection looks like:

But this is what I get as a result:

So, my chunk is totally incorrect. But what is more interesting is that on some arbitrary computers and in some arbitrary browsers chunks are correct. So, I really need to make this code cross-platform and cross-browser.


回答1:


But the problem is, it seems like from JavaScript code it is impossible to know this system/browser parameter - I mean initial page zoom level.

Actually there is window.devicePixelRatio
Just use it in place of ratio_W and ratio_H, and it starts working:

var initialZoom = devicePixelRatio;

var map = new ol.Map({
  layers: [new ol.layer.Tile({
    source: new ol.source.OSM()
  })],
  target: 'map',
  view: new ol.View({
    center: [-348792.96, 7170957.18],
    zoom: 10
  })
});

var draw = new ol.interaction.Draw({
   features: new ol.Collection(),
   type: "LineString",
   style: function (feature) {
       var style = [
         new ol.style.Style({
           fill: new ol.style.Fill({
             color: 'rgba(142,142,142,0.5)'
           }),
           stroke: new ol.style.Stroke({
             color: 'rgba(142,142,142,1)',
             lineDash: [4,4],
             width: 2
           })
         })
       ];
       return style;
   },
   geometryFunction: function (coordinates, geom) {
       if (!geom) {
           geom = new ol.geom.Polygon(null);
       }
       var start = coordinates[0];
       var end = coordinates[1];
       var mapExtent = map.getView().calculateExtent(map.getSize());

       var chunk = [start, [start[0], end[1]], end, [end[0], start[1]],start];
       var coords = [[[mapExtent[0],mapExtent[1]],[mapExtent[0],mapExtent[3]],[mapExtent[2],mapExtent[3]],[mapExtent[2],mapExtent[1]],[mapExtent[0],mapExtent[1]]], chunk];
       map.exportExtent = coordinates; // all you need
       geom.setCoordinates(coords);
       return geom;
   },
   maxPoints: 2
});

var canvas = map.getViewport().firstChild;

draw.on("drawend", function (e) {
    var image = new Image(), 
        link = document.getElementById("export-png");
   
    var topLeft = map.getPixelFromCoordinate(map.exportExtent[0]);
    var bottomRight = map.getPixelFromCoordinate(map.exportExtent[1]);
    
    var sx = topLeft[0];
    var sy = topLeft[1];
    var dw = bottomRight[0] - topLeft[0];
    var dh = bottomRight[1] - topLeft[1];
    
    image.id = "pic";
    image.crossOrigin = "anonymous";
    image.onload = function () { 
        sx = sx * initialZoom;
        sy = sy * initialZoom;
        dw = dw * initialZoom;
        dh = dh * initialZoom;
        link.href = crop(image, sx, sy, dw, dh);
        link.click();
    };
    
    image.src = canvas.toDataURL("image/png");
});

map.addInteraction(draw);

function crop(img, sx, sy, dw, dh) {
   var canvas = document.createElement('canvas');
   var ctx = canvas.getContext('2d');
   canvas.width = dw;
   canvas.height = dh;
   ctx.drawImage(img, sx, sy, dw, dh, 0, 0, dw, dh);
   return canvas.toDataURL("image/png");
}
<link href="https://openlayers.org/en/v4.6.4/css/ol.css" rel="stylesheet"/>
<script src="https://openlayers.org/en/v4.6.4/build/ol-debug.js"></script>
<div id="map" class="map"></div> 
<a id="export-png" class="btn" download="map.png">&nbsp;</a>

Snippet comes from @dube's answer, and I modified image.onload only and stored initialZoom in the first line.




回答2:


I would have guessed that you mixed projections. But that was not the bug, you simply mixed up some variables. It's the unecessary complicated way of storing the selection extent.

coordinate of geometryFunction already has the extent, defined as two coordinates. Just use those and remove the loop. No idea why it's there in the first place.

var map = new ol.Map({
  layers: [new ol.layer.Tile({
    source: new ol.source.OSM()
  })],
  target: 'map',
  view: new ol.View({
    center: [-348792.96, 7170957.18],
    zoom: 10
  })
});

var draw = new ol.interaction.Draw({
   features: new ol.Collection(),
   type: "LineString",
   style: function (feature) {
       var style = [
         new ol.style.Style({
           fill: new ol.style.Fill({
             color: 'rgba(142,142,142,0.5)'
           }),
           stroke: new ol.style.Stroke({
             color: 'rgba(142,142,142,1)',
             lineDash: [4,4],
             width: 2
           })
         })
       ];
       return style;
   },
   geometryFunction: function (coordinates, geom) {
       if (!geom) {
           geom = new ol.geom.Polygon(null);
       }
       var start = coordinates[0];
       var end = coordinates[1];
       var mapExtent = map.getView().calculateExtent(map.getSize());

       var chunk = [start, [start[0], end[1]], end, [end[0], start[1]],start];
       var coords = [[[mapExtent[0],mapExtent[1]],[mapExtent[0],mapExtent[3]],[mapExtent[2],mapExtent[3]],[mapExtent[2],mapExtent[1]],[mapExtent[0],mapExtent[1]]], chunk];
       map.exportExtent = coordinates; // all you need
       geom.setCoordinates(coords);
       return geom;
   },
   maxPoints: 2
});

var canvas = map.getViewport().firstChild;

draw.on("drawend", function (e) {
    var image = new Image(), 
        link = document.getElementById("export-png");
   
    var topLeft = map.getPixelFromCoordinate(map.exportExtent[0]);
    var bottomRight = map.getPixelFromCoordinate(map.exportExtent[1]);
    
    var sx = topLeft[0];
    var sy = topLeft[1];
    var dw = bottomRight[0] - topLeft[0];
    var dh = bottomRight[1] - topLeft[1];
    
    image.id = "pic";
    image.crossOrigin = "anonymous";
    image.onload = function () { 
        var ratio_W = canvas.clientWidth / image.naturalWidth;
        var ratio_H = canvas.clientHeight / image.naturalHeight;
        //with or without scaling image chunk is incorrect
        sx = sx * ratio_W;
        sy = sy * ratio_H;
        dw = dw * ratio_W;
        dh = dh * ratio_H;
        // console.log(sx, sy, dw, dh, ratio_H);
        link.href = crop(image, sx, sy, dw, dh);
        link.click();
    };
    
    image.src = canvas.toDataURL("image/png");
});

map.addInteraction(draw);

function crop(img, sx, sy, dw, dh) {
   var canvas = document.createElement('canvas');
   var ctx = canvas.getContext('2d');
   canvas.width = dw;
   canvas.height = dh;
   ctx.drawImage(img, sx, sy, dw, dh, 0, 0, dw, dh);
   return canvas.toDataURL("image/png");
}
<link href="https://openlayers.org/en/v4.6.4/css/ol.css" rel="stylesheet"/>
<script src="https://openlayers.org/en/v4.6.4/build/ol-debug.js"></script>
<div id="map" class="map"></div> 
<a id="export-png" class="btn" download="map.png">&nbsp;</a>


来源:https://stackoverflow.com/questions/48962400/unable-to-make-a-chunk-from-canvas

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!