<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      canvas實(shí)現(xiàn)摳圖,畫(huà)筆,水印等功能

      <!DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="UTF-8" />
          <meta name="viewport" content="width=device-width, initial-scale=1.0" />
          <title>Document</title>
          <style>
            html,
            body {
              width: 100%;
              height: 100%;
              display: flex;
              align-items: center;
              justify-content: center;
              padding: 0;
              margin: 0;
            }
            .canvas-box {
              display: flex;
              position: relative; 
            }
            #canvas {
              box-shadow: 0 0 0 1px #ccc;
            }
            .option {
              padding-left: 12px;
            }
            .item {
              display: flex;
              align-items: center;
              margin-bottom: 6px;
            }
            .btn-box {
              width: 300px;
              display: flex;
              align-items: center;
              justify-content: center;
              flex-wrap: wrap;
            }
            .cursor {
              width: 20px;
              height: 20px;
              position: absolute;
              border-radius: 50%;
              transform: translate(-50%, -50%);
              box-shadow: 0 0 0 2px #000 inset;
              background-color: transparent;
              pointer-events: none;
              opacity: 0;
              z-index: 99;
            }
          </style>
        </head>
        <body>
          <div class="canvas-box">
            <div class="cursor"></div>
            <canvas id="canvas" style="background-color: rgba(0, 0, 0, 0)"></canvas>
          </div>
          <div class="option">
            <div class="item">
              畫(huà)筆大小(2~50):
              <input type="number" name="pencil" id="" min="2" max="50" value="20" />
            </div>
            <div class="item">
              橡皮大小(2~50):
              <input type="number" name="eraser" id="" min="2" max="50" value="20" />
            </div>
            <div class="item">
              保存圖片質(zhì)量(0.4~1):
              <input
                type="number"
                name="quality"
                id=""
                min="0.4"
                max="1"
                value="0.8"
                step="0.1"
              />
            </div>
            <div class="item">
              保存格式:
              <input type="radio" data-type="imgType" name="png" id="" checked />png
              <input type="radio" data-type="imgType" name="jpeg" id="" />jpeg
              <input type="radio" data-type="imgType" name="webp" id="" />webp
            </div>
            <div class="item">
              畫(huà)布隨上傳圖片縮放:
              <input type="radio" data-type="imgRule" name="long" checked />長(zhǎng)邊
              <input type="radio" data-type="imgRule" name="short" />短邊
              <input type="radio" data-type="imgRule" name="custom" />自適應(yīng)
            </div>
            <div class="item">
              水印文案:<input
                type="text"
                name="waterMark"
                value="大吉大利,今晚吃雞"
              />
            </div>
            <div class="item">
              水印文案顏色:<input type="color" name="waterMarkColor" />
            </div>
            <div class="item">
              水印透明度(0.1~1):
              <input
                type="number"
                name="waterMarkOpacity"
                id=""
                min="0.1"
                max="1"
                value="0.15"
                step="0.01"
              />
            </div>
            <div class="item">
              水印文字大小(10~60):
              <input
                type="number"
                name="fontSize"
                id=""
                min="12"
                max="60"
                value="14"
              />
            </div>
            <div class="item">
              水印旋轉(zhuǎn)角度(-360~360):
              <input
                type="number"
                name="rotate"
                id=""
                min="-360"
                max="360"
                value="-25"
              />
            </div>
            <div class="item">
              水印水平間距(50~500):
              <input type="number" name="x" id="" min="20" max="500" value="100" />
            </div>
            <div class="item">
              水印垂直間距(50~500):
              <input type="number" name="y" id="" min="20" max="500" value="100" />
            </div>
            <div class="item">
              水印X軸偏移量(50~500):
              <input
                type="number"
                name="offsetX"
                id=""
                min="50"
                max="300"
                value="50"
              />
            </div>
            <div class="item">
              水印Y軸偏移量(50~500):
              <input
                type="number"
                name="offsetY"
                id=""
                min="50"
                max="300"
                value="50"
              />
            </div>
            <div class="item">
              水印文案最大寬度(50~500):
              <input
                type="number"
                name="maxWidth"
                id=""
                min="50"
                max="300"
                value="200"
              />
            </div>
            <div class="btn-box">
              <button name="usePencil">使用畫(huà)筆</button>
              <button name="useEraser">使用橡皮</button>
              <input type="color" name="color" id="" />
              <button name="reset">清空畫(huà)布</button>
              <button name="undo">撤銷</button>
              <button name="redo">恢復(fù)</button>
              <button name="save">保存</button>
              <button name="upload">上傳圖片</button>
              <button name="waterMark">使用水印</button>
              <button name="cutout">摳圖</button>
            </div>
          </div>
          <input
            type="file"
            name="file"
            accept="image/jpeg,image/png,image/webp"
            style="display: none"
          />
          <script>
            const container = document.querySelector(".canvas-box");
            const cursorDom = document.querySelector(".cursor");
            const fileDom = document.querySelector('input[type="file"]');
            const canvas = document.querySelector("#canvas");
            const ctx = canvas.getContext("2d", { willReadFrequently: true });
            const cursor = document.querySelector(".cursor");
            const usePencil = document.querySelector('button[name="usePencil"]');
            const useEraser = document.querySelector('button[name="useEraser"]');
            const pencilRange = document.querySelector('input[name="pencil"]');
            const eraserRange = document.querySelector('input[name="eraser"]');
            const qualityRange = document.querySelector('input[name="quality"]');
            const colorPick = document.querySelector('input[name="color"]');
            const cutout = document.querySelector('button[name="cutout"]');
            const waterMarkColorPick = document.querySelector(
              'input[name="waterMarkColor"]'
            );
            const waterMarkOpacityRange = document.querySelector(
              'input[name="waterMarkOpacity"]'
            );
            const imgTypeRadios = document.querySelectorAll(
              'input[data-type="imgType"]'
            );
            const imgRuleRadios = document.querySelectorAll(
              'input[data-type="imgRule"]'
            );
            const fontSizeRange = document.querySelector('input[name="fontSize"]');
            const xRange = document.querySelector('input[name="x"]');
            const yRange = document.querySelector('input[name="y"]');
            const rotateRange = document.querySelector('input[name="rotate"]');
            const offsetRangeX = document.querySelector('input[name="offsetX"]');
            const offsetRangeY = document.querySelector('input[name="offsetY"]');
            const maxWidthRange = document.querySelector('input[name="maxWidth"]');
            const reset = document.querySelector('button[name="reset"]');
            const undo = document.querySelector('button[name="undo"]');
            const redo = document.querySelector('button[name="redo"]');
            const save = document.querySelector('button[name="save"]');
            const upload = document.querySelector('button[name="upload"]');
            const waterMark = document.querySelector('button[name="waterMark"]');
      
            const canvasDefaultSize = 600;
            canvas.width = canvasDefaultSize;
            canvas.height = canvasDefaultSize;
            let pencil = 20;
            let eraser = 20;
            let quality = 0.8;
            let imgType = "png";
            let imgRule = "long";
            let isPencil = true;
            let isEraser = false;
            let isDrawingLine = false;
            let colors = "#000";
            let historyIdx = 0;
            let history = [];
            let canvasArea = [0, 0, canvas.width, canvas.height];
            let text = "大吉大利,今晚吃雞";
            let rotate = -25;
            let maxWidth = 200;
            let offsetX = 50;
            let offsetY = 50;
            let gap = [100, 100];
      let isCutout=false
            const setCursorSize = (size) => {
              cursor.style.width = size + "px";
              cursor.style.height = size + "px";
            };
            cutout.onclick=(e)=>{
              isCutout=!isCutout
              if(isCutout){
                cutout.innerHTML='取消摳圖'
              }else{
                cutout.innerHTML='摳圖'
              }
      
            }
            pencilRange.oninput = (e) => {
              if (!e.target.value) e.target.value = 20;
              pencil = e.target.valueAsNumber;
              isPencil && setCursorSize(pencil);
            };
      
            eraserRange.oninput = (e) => {
              if (!e.target.value) e.target.value = 20;
              eraser = e.target.valueAsNumber;
              isEraser && setCursorSize(eraser);
            };
      
            qualityRange.oninput = (e) => {
              if (!e.target.value) e.target.value = 0.8;
              quality = e.target.valueAsNumber;
            };
      
            fontSizeRange.oninput = (e) => {
              if (!e.target.value) e.target.value = 14;
              ctx.font = `500 ${e.target.valueAsNumber}px sans-serif`;
            };
      
            xRange.oninput = (e) => {
              if (!e.target.value) e.target.value = 100;
              gap[0] = e.target.valueAsNumber;
            };
      
            yRange.oninput = (e) => {
              if (!e.target.value) e.target.value = 100;
              gap[1] = e.target.valueAsNumber;
            };
      
            rotateRange.oninput = (e) => {
              if (Number.isNaN(e.target.valueAsNumber)) e.target.value = -25;
              rotate = e.target.valueAsNumber;
            };
      
            offsetRangeX.oninput = (e) => {
              if (Number.isNaN(e.target.valueAsNumber)) e.target.value = 50;
              offsetX = e.target.valueAsNumber;
            };
      
            offsetRangeY.oninput = (e) => {
              if (Number.isNaN(e.target.valueAsNumber)) e.target.value = 50;
              offsetY = e.target.valueAsNumber;
            };
      
            maxWidthRange.oninput = (e) => {
              if (Number.isNaN(e.target.valueAsNumber)) e.target.value = 200;
              maxWidth = e.target.valueAsNumber;
            };
      
            waterMarkOpacityRange.oninput = (e) => {
              const [r, g, b] = hex2Rgb(waterMarkColorPick.value);
              ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${waterMarkOpacityRange.valueAsNumber})`;
            };
      
            fileDom.oninput = (e) => {
              const fileReader = new FileReader();
              fileReader.readAsDataURL(e.target.files[0]);
              fileReader.onload = (e) => {
                const img = document.createElement("img");
                img.src = e.target.result;
                img.onload = () => {
                  let wh = [img.width, img.height];
                  let canvasWh = [canvasDefaultSize, canvasDefaultSize];
                  if (imgRule === "long") {
                    const ratio = canvasDefaultSize / Math.max(...wh);
                    wh = [img.width * ratio, img.height * ratio];
                  } else if (imgRule === "short") {
                    const ratio = canvasDefaultSize / Math.min(...wh);
                    wh = [img.width * ratio, img.height * ratio];
                  } else {
                    canvasWh = [img.width, img.height];
                  }
                  ctx.clearRect(...canvasArea);
                  canvasArea = [0, 0, ...canvasWh];
                  canvas.width = canvasWh[0];
                  canvas.height = canvasWh[1];
                  ctx.drawImage(img, 0, 0, ...wh);
                };
              };
            };
      
            imgTypeRadios.forEach((el) => {
              el.onclick = () => {
                imgTypeRadios.forEach((el2) => (el2.checked = false));
                el.checked = true;
                imgType = el.name;
              };
            });
      
            imgRuleRadios.forEach((el) => {
              el.onclick = () => {
                imgRuleRadios.forEach((el2) => (el2.checked = false));
                el.checked = true;
                imgRule = el.name;
              };
            });
      
            colorPick.oninput = (e) => (colors = e.target.value);
      
            waterMarkColorPick.oninput = (e) => {
              const [r, g, b] = hex2Rgb(e.target.value);
              ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${waterMarkOpacityRange.valueAsNumber})`;
            };
      
            // 撤銷
            undo.onclick = () => {
              historyIdx--;
              if (historyIdx <= -1) {
                historyIdx = -1;
                ctx.clearRect(...canvasArea);
              } else {
                ctx.putImageData(history[historyIdx], 0, 0);
              }
            };
      
            // 恢復(fù)
            redo.onclick = () => {
              historyIdx++;
              if (historyIdx > history.length - 1) historyIdx = history.length - 1;
              else ctx.putImageData(history[historyIdx], 0, 0);
            };
      
            save.onclick = () => {
              const a = document.createElement("a");
              a.href = canvas.toDataURL(`image/${imgType}`, quality);
              a.download = `save_${Date.now()}`;
              document.body.append(a);
              a.click();
              a.remove();
            };
      
            upload.onclick = () => {
              fileDom.value = "";
              fileDom.click();
            };
      
            usePencil.onclick = () => {
              isPencil = true;
              isEraser = false;
              setCursorSize(pencil);
            };
      
            useEraser.onclick = () => {
              isPencil = false;
              isEraser = true;
              setCursorSize(eraser);
            };
      
            reset.onclick = () => {
              history = [];
              historyIdx = -1;
              ctx.clearRect(...canvasArea);
              canvas.width = canvasDefaultSize;
              canvas.height = canvasDefaultSize;
              canvasArea = [0, 0, canvas.width, canvas.height];
              const [r, g, b] = hex2Rgb(waterMarkColorPick.value);
              waterMarkOpacityRange.oninput();
              ctx.font = `500 ${fontSizeRange.valueAsNumber}px sans-serif`;
            };
      
            waterMark.onclick = () => {
              let [x, y] = gap;
              let [, , w, h] = canvasArea;
              const xLine = Math.ceil((w - offsetX) / x);
              const yLine = Math.ceil((h - offsetY) / y);
              waterMarkOpacityRange.oninput();
              ctx.font = `500 ${fontSizeRange.valueAsNumber}px sans-serif`;
              for (let i = 0; i <= xLine; i++) {
                const x0 = x * i + offsetX;
                for (let j = 0; j <= yLine; j++) {
                  drawWaterMark(x0, y * j + offsetY);
                }
              }
            };
            const drawWaterMark = (x, y) => {
              ctx.save();
              ctx.translate(x, y);
              ctx.rotate((rotate / 180) * Math.PI);
              ctx.fillText(text, -ctx.measureText(text).width / 2, 0, maxWidth);
              ctx.restore();
            };
      
            canvas.onmousedown = (e) => {
              // 只允許左鍵
              if (e.button) return;
              if (!isPencil && !isEraser) return false;
              isDrawingLine = true;
              ctx.beginPath();
              ctx.globalCompositeOperation = isEraser
                ? "destination-out"
                : "source-over";
              ctx.strokeStyle = isEraser ? "#fff" : colors;
              if(isCutout){
                ctx.globalCompositeOperation = 'destination-out';
                ctx.strokeStyle=undefined
              }
              
              ctx.lineCap = "round";
              ctx.lineJoin = "round";
              ctx.lineWidth = isEraser ? eraser : pencil;
              ctx.moveTo(e.offsetX, e.offsetY);
              ctx.lineTo(e.offsetX, e.offsetY);
              ctx.stroke();
              canvas.onmousemove = composeCanvasMousemove;
            };
      
            canvas.onmouseup = () => {
              if (!isPencil && !isEraser) return false;
              isDrawingLine = false;
              canvas.onmousemove = null;
              // 使用了橡皮時(shí),必須存在歷史記錄,否則不做任何事
              if (isEraser && !history.length) return false;
              addOneHistory(lineToAlpha());
            };
      
            container.onmouseenter = (e) => {
              if (!container.contains(e.toElement)) return;
              if (isPencil || isEraser) {
                // 離開(kāi)畫(huà)布時(shí),若正在使用劃線功能,重新開(kāi)啟一個(gè)路徑
                if (isDrawingLine) {
                  ctx.beginPath();
                  ctx.moveTo(e.offsetX, e.offsetY);
                  ctx.lineTo(e.offsetX, e.offsetY);
                  ctx.stroke();
                }
                canvas.style.cursor = "none";
                cursorDom.style.opacity = "1";
                cursorDom.style.left = e.offsetX + "px";
                cursorDom.style.top = e.offsetY + "px";
                cursorDom.style.boxShadow = `0 0 0 2px ${
                  isPencil ? colors : "#ccc"
                } inset`;
              } else canvas.style.cursor = "default";
            };
      
            container.onmousemove = (e) => {
              if (isPencil || isEraser) {
                cursorDom.style.opacity = "1";
                cursorDom.style.left = e.offsetX + "px";
                cursorDom.style.top = e.offsetY + "px";
                return false;
              }
            };
      
            container.onmouseout = (e) => {
              if (!container.contains(e.fromElement)) return;
              cursorDom.style.opacity = "0";
            };
      
            const composeCanvasMousemove = (e) => {
              ctx.lineTo(e.offsetX, e.offsetY);
              ctx.stroke();
            };
      
            const lineToAlpha = () => {
              const imageData = ctx.getImageData(...canvasArea);
              return imageData;
            };
      
            // 添加一項(xiàng)歷史記錄
            const addOneHistory = (json) => {
              if (history.length - 1 !== historyIdx) {
                history = history.slice(0, historyIdx + 1);
              }
              historyIdx = history.push(json) - 1;
            };
      
            // hex 2 rgb
            const hex2Rgb = (hex) => {
              const red = hex.substring(1, 3);
              const green = hex.substring(3, 5);
              const blue = hex.substring(5, 7);
              return [parseInt(red, 16), parseInt(green, 16), parseInt(blue, 16)];
            };
      
            // 鼠標(biāo)抬起時(shí),發(fā)現(xiàn)不處于畫(huà)布內(nèi),調(diào)用畫(huà)筆結(jié)束
            const handleLeaveCanvas = (e) => {
              if (!isDrawingLine) return;
              if (!container.contains(e.target)) canvas.onmouseup();
            };
            window.addEventListener("mouseup", handleLeaveCanvas);
      
            setCursorSize(pencil);
          </script>
        </body>
      </html>

       

      posted @ 2024-11-18 17:40  雅痞_yuppie  閱讀(111)  評(píng)論(0)    收藏  舉報(bào)
      主站蜘蛛池模板: 亚洲春色在线视频| 女人张开腿无遮无挡视频| 亚洲欧洲日产国无高清码图片| 无码免费大香伊蕉在人线国产 | 国产成人啪精品午夜网站| 亚洲中少妇久久中文字幕| 久久天堂综合亚洲伊人HD妓女 | 色综合中文综合网| 久久亚洲国产五月综合网| 性色av无码久久一区二区三区| 深夜免费av在线观看| 国产精品一二三中文字幕| 久爱无码精品免费视频在线观看| 国产真实野战在线视频| 国产蜜臀精品一区二区三区| 国产精品久久久久7777| 亚洲成人av综合一区| 黑人av无码一区| 久久国产精品成人影院| 免费十八禁一区二区三区| 成人午夜免费无码视频在线观看 | 日韩一区二区三区女优丝袜| 久久久久无码精品国产h动漫| 日本高清视频网站www| 内射老妇bbwx0c0ck| 日韩av熟女人妻一区二| 将乐县| 九九热精品免费在线视频| 麻豆一区二区中文字幕| 国产久免费热视频在线观看 | 中文字幕av一区| 亚洲成人精品综合在线| 国产人妻精品无码av在线| 国产又爽又黄又无遮挡的激情视频 | 亚洲国产欧美在线人成| 欧美人与动牲交A免费观看| 欧美性大战xxxxx久久久| 国产二区三区不卡免费| 无码精品人妻一区二区三区湄公河| 免费观看国产女人高潮视频| 国产一区二区波多野结衣|