asimryu

canvas image drag&drop, merge

Jul 27th, 2020 (edited)
179
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
HTML 8.04 KB | None | 0 0
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <title>canvas drag & drop, merge images</title>
  6.    <style>
  7.        canvas { border:1px solid #888; }
  8.         #canvas2 { display: none; }
  9.         .btns { margin-top: 10px; }
  10.         .btns button { width: auto; height: 30px; padding: 0 10px; background-color: #fff; border: 1px solid #888; cursor: pointer; }
  11.         .btns button.active { background-color: #ff0; }
  12.        
  13.     </style>
  14. </head>
  15. <body>
  16. <div>
  17.     <p>이미지를 두 개 이상 파일에서 선택해 캔버스에 그릴 수 있습니다.<br>캔버스에 그려진 이미지를 하나씩 마우스로 누른채로 이동시킬 수 있습니다.<br>이미지를 두 개까지 선택할 수 있습니다.<br>선택된 이미지를 살제할 수 있습니다.</p>
  18. </div>
  19. <canvas id="canvas" width="800" height="500"></canvas>
  20. <div><input type="file" id="photo" accept="image/*"></div>
  21. <div class="btns">
  22.     <button class='action btn-draganddrop active' data-action='dragndrop'>Drag & Drop</button>
  23.    <button class='action btn-select' data-action='select'>이미지선택</button>
  24.    <button class='action btn-merge' data-action='merge'>합치기</button>
  25.    <button class='action btn-delete' data-action='delete'>삭제</button>
  26. </div>
  27. <canvas id="canvas2" width="800" height="500"></canvas>
  28. <script src="http://code.jquery.com/jquery-3.5.1.min.js"></script>
  29. <script>
  30. const canvas = document.getElementById("canvas");
  31. const ctx = canvas.getContext("2d");
  32. const canvas2 = document.getElementById("canvas2");
  33. const ctx2 = canvas2.getContext("2d");
  34. const canvasOffset = $("#canvas").offset();
  35. const offsetX = canvasOffset.left;
  36. const offsetY = canvasOffset.top;
  37. const cw = canvas.width;
  38. const ch = canvas.height;
  39. let isDown = false;
  40. let lastX;
  41. let lastY;
  42. let images = [];
  43. let hit = -1;
  44. let action = "dragndrop";
  45. let selections = [];
  46.  
  47. const drawAll = () => {
  48.     ctx.clearRect(0, 0, cw, ch);
  49.     for (let i = 0; i < images.length; i++) {
  50.        let image = images[i];
  51.        ctx.drawImage(image.img, image.x, image.y, image.w, image.h);
  52.        ctx.closePath();
  53.        if( action === "select" ) {
  54.            if( selections.includes(i) ) {
  55.                ctx.strokeStyle = "#f00";
  56.                ctx.setLineDash([5]);
  57.                ctx.rect(image.x, image.y, image.w, image.h);
  58.                ctx.stroke();
  59.            }
  60.        }
  61.    }
  62. }
  63.  
  64. const handleMouseDown = e => {
  65.     e.preventDefault();
  66.     e.stopPropagation();
  67.     lastX = parseInt(e.clientX - offsetX);
  68.     lastY = parseInt(e.clientY - offsetY);
  69.     hit = getImageIndxByPosition(e);
  70.     if (hit >= 0) {
  71.         if( action === "dragndrop" ) {
  72.             isDown = true;
  73.             $("#canvas").css("cursor","move");
  74.         } else if( action === "select" ) {
  75.             $("#canvas").css("cursor","pointer");
  76.             if( ! selections.includes(hit) ) {
  77.                 if( selections.length == 2 ) {
  78.                     alert("두 개까지만 선택할 수 있습니다.");
  79.                 } else {
  80.                     selections.push(hit);
  81.                     drawAll();                    
  82.                 }
  83.             }
  84.         }      
  85.     }  
  86. }
  87.  
  88. const handleMouseUp = e => {
  89.     e.preventDefault();
  90.     e.stopPropagation();
  91.     hit = -1;
  92.     isDown = false;
  93.     $("#canvas").css("cursor","default");
  94. }
  95.  
  96. const handleMouseMove = e => {
  97.     if (!isDown || hit < 0 ) return;
  98.    e.preventDefault();
  99.    e.stopPropagation();
  100.    mouseX = parseInt(e.clientX - offsetX);
  101.    mouseY = parseInt(e.clientY - offsetY);
  102.    let dx = mouseX - lastX;
  103.    let dy = mouseY - lastY;
  104.    lastX = mouseX;
  105.    lastY = mouseY;
  106.    images[hit].x += dx;
  107.    images[hit].y += dy;
  108.    drawAll();    
  109. }    
  110.  
  111. const getImageIndxByPosition = e => {
  112.     hit = -1;
  113.     for (let i = 0; i < images.length; i++) {
  114.        let image = images[i];
  115.        if (lastX > image.x && lastX < (image.x + image.w) && lastY > image.y && lastY < (image.y + image.h)) {
  116.            hit = i;
  117.         }
  118.     }
  119.     return hit;
  120. }
  121.  
  122. const handlePhotoSelect = e => {
  123.     let photo = e.target.files[0];
  124.     if( ! photo ) return;
  125.     const img = new Image();
  126.     img.onload = () => {
  127.         let imgdata = {};
  128.         imgdata.w = img.width;
  129.         imgdata.h = img.height;
  130.         imgdata.x = 0;
  131.         imgdata.y = 0;
  132.         imgdata.img = img;
  133.         ctx.drawImage(img, 0, 0, imgdata.w, imgdata.h);
  134.         images.push(imgdata);
  135.     }
  136.     const reader = new FileReader();
  137.     reader.onload = file => {
  138.      img.src = file.target.result;
  139.     }
  140.     reader.readAsDataURL(photo);
  141. }
  142.  
  143. const handleActions = obj => {
  144.     action = $(obj).attr("data-action");
  145.     $("div.btns button").removeClass("active");
  146.     $(obj).addClass("active");
  147.     if( action === "select" ) {
  148.         selections = [];
  149.         ctx.beginPath();
  150.         drawAll();
  151.     } else if( action === "merge" ) {
  152.         if( selections.length < 2 ) {
  153.            alert("합치기를 하려면 2개의 이미지가 선택되어야 합니다.");
  154.            $("div.btns button.btn-draganddrop").click();
  155.            return;
  156.        } else {
  157.            if( checkOverlap() ) {
  158.                mergePhoto();    
  159.            } else {
  160.                alert("합치기를 하려면 2개의 이미지가 일부라도 겹쳐야 합니다.");
  161.                drawAll();
  162.                $("div.btns button.btn-draganddrop").click();
  163.            }
  164.            
  165.        }
  166.    } else if( action === "delete" ) {
  167.        if( selections.length < 1 ) {
  168.            alert("삭제 하려면 1개의 이미지가 선택되어야 합니다.");
  169.            $("div.btns button.btn-draganddrop").click();
  170.            return;
  171.        } else {
  172.            let images2 = [];
  173.            for(let i=0; i<images.length; i++ ) {
  174.                if( ! selections.includes(i) ) images2.push(images[i]);
  175.            }
  176.            images = images2;
  177.            drawAll();
  178.            $("div.btns button.btn-draganddrop").click();
  179.        }
  180.    }
  181. }
  182.  
  183. const checkOverlap = () => {
  184.     if( selections.length < 2 ) return false;
  185.    return !( images[0].x > (images[1].x + images[1].w) ||
  186.              (images[0].x + images[0].w) <  images[1].x ||
  187.              images[0].y > (images[1].y + images[1].h) ||
  188.              (images[0].y + images[0].h) <  images[1].y );
  189. }
  190.  
  191. const mergePhoto = () => {
  192.     ctx.clearRect(0, 0, cw, ch);
  193.     let xs = [];
  194.     let ys = [];
  195.     for(let i = 0; i < selections.length; i++ ) {
  196.        let tmpimg = images[selections[i]];
  197.        ctx2.drawImage(tmpimg.img, tmpimg.x, tmpimg.y, tmpimg.w, tmpimg.h);
  198.        xs.push(tmpimg.x);
  199.        xs.push(tmpimg.x + tmpimg.w);
  200.        ys.push(tmpimg.y);
  201.        ys.push(tmpimg.y + tmpimg.h);
  202.    }
  203.    let xmin = Math.min.apply(null, xs);
  204.    let xmax = Math.max.apply(null, xs);
  205.    let ymin = Math.min.apply(null, ys);
  206.    let ymax = Math.max.apply(null, ys);
  207.    let crop_width = xmax - xmin;
  208.    let crop_height = ymax - ymin;
  209.    let crop_img = ctx2.getImageData(xmin, ymin, crop_width, crop_height);
  210.    let canvas3 = document.createElement("canvas");
  211.    canvas3.width = crop_width;
  212.    canvas3.height = crop_height;
  213.    let ctx3 = canvas3.getContext("2d");
  214.    ctx3.putImageData(crop_img, 0, 0);
  215.    let img = new Image();
  216.    img.onload = () => {
  217.         let images2 = [];
  218.         for(let i=0; i<images.length; i++ ) {
  219.            if( ! selections.includes(i) ) images2.push(images[i]);
  220.        }
  221.        images2.push({img:img, x:xmin, y: ymin, w:crop_width, h:crop_height});
  222.        images = images2;
  223.        drawAll();
  224.        $("div.btns button.btn-draganddrop").click();
  225.    }
  226.    img.src = canvas3.toDataURL("image/png");
  227. }
  228.  
  229. $("#canvas").mousedown(function (e) { handleMouseDown(e); });
  230. $("#canvas").mousemove(function (e) { handleMouseMove(e); });
  231. $("#canvas").mouseup(function (e) { handleMouseUp(e); });
  232. $("#canvas").mouseout(function (e) { handleMouseUp(e); });
  233. $("#photo").change(function (e) { handlePhotoSelect(e); });    
  234. $("div.btns button").click(function () { handleActions(this); });        
  235.    
  236. </script>
  237. </body>
  238. </html>
Add Comment
Please, Sign In to add comment