Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
319 views
in Technique[技术] by (71.8m points)

javascript - Crop the image in irregular shape and stretch it

I found images that depict what is my problem:

enter image description here

enter image description here

User will able to choose four points on canvas to crop the part of image and than stretch it.

How to do that in HTML5? drawImage function (as I know) works only with rectangles (takes x, y, width and height values) so I can't use irregular shape. The solution have to work in every modern browser, so I don't want things based on webgl or something.

EDIT: More info: this will be app for editing pictures. I want to let user cut some part of bigger picture and edit that. It will be similar to Paint, so canvas is required to edit pixels.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The effect you're going for is "perspective warping".

Canvas's 2D context cannot do this "out-of-the-box" because it can't turn a rectangle into a trapezoid. Canvas 2D can only do affine transforms which can only form parallelograms.

As user @Canvas says, Canvas 3D (webgl) can do the transforms you're going for.

I did this a while back. It uses Canvas 2d and it redraws an image using 1 pixel wide vertical slices which are stretched to "fake" a perspective warp. You are welcome to use it as a starting point for your project.

enter image description here enter image description here

Example code and a Demo: http://jsfiddle.net/m1erickson/y4kst2pk/

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
    body{ background-color: ivory; }
    #canvas{border:1px solid red;}
</style>
<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");
    var $canvas=$("#canvas");
    var canvasOffset=$canvas.offset();
    var offsetX=canvasOffset.left;
    var offsetY=canvasOffset.top;
    var scrollX=$canvas.scrollLeft();
    var scrollY=$canvas.scrollTop();

    //
    var isDown=false;
    var PI2=Math.PI*2;
    var selectedGuide=-1;
    var guides=[];


    //
    var marginLeft=50;
    var marginTop=50;
    var iw,ih,cw,ch;
    var img=new Image();
    img.onload=start;
    img.src='https://dl.dropboxusercontent.com/u/139992952/stack1/buildings1.jpg';
    function start(){

        iw=img.width;
        ih=img.height;
        canvas.width=iw+100;
        canvas.height=ih+100;
        cw=canvas.width;
        ch=canvas.height;
        ctx.strokeStyle="blue";
        ctx.fillStyle="blue";

        guides.push({x:0,y:0,r:10});
        guides.push({x:0,y:ih,r:10});
        guides.push({x:iw,y:0,r:10});
        guides.push({x:iw,y:ih,r:10});

        //
        $("#canvas").mousedown(function(e){handleMouseDown(e);});
        $("#canvas").mousemove(function(e){handleMouseMove(e);});
        $("#canvas").mouseup(function(e){handleMouseUp(e);});
        $("#canvas").mouseout(function(e){handleMouseOut(e);});

        drawAll();
    }

    function drawAll(){
        ctx.clearRect(0,0,cw,ch);
        drawGuides();
        drawImage();
    }

    function drawGuides(){
        for(var i=0;i<guides.length;i++){
            var guide=guides[i];
            ctx.beginPath();
            ctx.arc(guide.x+marginLeft,guide.y+marginTop,guide.r,0,PI2);
            ctx.closePath();
            ctx.fill();
        }
    }

    function drawImage(){

        // TODO use guides 
        var x1=guides[0].x;
        var y1=guides[0].y;
        var x2=guides[2].x;
        var y2=guides[2].y;
        var x3=guides[1].x;
        var y3=guides[1].y;
        var x4=guides[3].x;
        var y4=guides[3].y;


        // calc line equations slope & b (m,b)
        var m1=Math.tan( Math.atan2((y2-y1),(x2-x1)) );
        var b1=y2-m1*x2;
        var m2=Math.tan( Math.atan2((y4-y3),(x4-x3)) );
        var b2=y4-m2*x4;
        // draw vertical slices
        for(var X=0;X<iw;X++){
            var yTop=m1*X+b1;
            var yBottom=m2*X+b2;
            ctx.drawImage( img,X,0,1,ih, 
                X+marginLeft,yTop+marginTop,1,yBottom-yTop );
        }

        // outline
        ctx.save();
        ctx.translate(marginLeft,marginTop);
        ctx.beginPath();
        ctx.moveTo(x1,y1);
        ctx.lineTo(x2,y2);
        ctx.lineTo(x4,y4);
        ctx.lineTo(x3,y3);
        ctx.closePath();
        ctx.strokeStyle="black";
        ctx.stroke();
        ctx.restore();
    }



    function handleMouseDown(e){
      e.preventDefault();
      var mouseX=parseInt(e.clientX-offsetX);
      var mouseY=parseInt(e.clientY-offsetY);

      // Put your mousedown stuff here
      selectedGuide=-1;
      for(var i=0;i<guides.length;i++){
          var guide=guides[i];
          var dx=mouseX-(guide.x+marginLeft);
          var dy=mouseY-(guide.y+marginTop);
          if(dx*dx+dy*dy<=guide.r*guide.r){
              selectedGuide=i;
              break;
          }
      }
      isDown=(selectedGuide>=0);
    }

    function handleMouseUp(e){
      e.preventDefault();
      isDown=false;
    }

    function handleMouseOut(e){
      e.preventDefault();
      isDown=false;
    }

    function handleMouseMove(e){
      if(!isDown){return;}
      e.preventDefault();
      var x=parseInt(e.clientX-offsetX)-marginLeft;
      var y=parseInt(e.clientY-offsetY)-marginTop;
      var guide=guides[selectedGuide];
      guides[selectedGuide].y=y;
      if(selectedGuide==0 && y>guides[1].y){guide.y=guides[1].y;}
      if(selectedGuide==1 && y<guides[0].y){guide.y=guides[0].y;}
      if(selectedGuide==2 && y>guides[3].y){guide.y=guides[3].y;}
      if(selectedGuide==3 && y<guides[2].y){guide.y=guides[2].y;}
      drawAll();
    }

}); // end $(function(){});
</script>
</head>
<body>
    <h4>Perspective Warp by vertically dragging left or right blue guides.</h4>
    <canvas id="canvas" width=300 height=300></canvas>
</body>
</html>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...