• html 头像裁剪框


    稍微改改就能使用了,这里预览直接在裁剪框canvas上显示了,

    可以直接使用canvas.toBlob((result)=>{

        //result 就是blob  使用formdata包装一下就可以使用ajax等技术上传了

    const formData = new FormData();

    formData.append("file", result, "图像名称.jpg");

    });

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
      <title>图片裁剪</title>
      <style type="text/css">
      body {
        overflow: hidden;
      }
    
      .wrapper {
        position: relative;
        display: flex;
        width: 100%;
        max-width: 500px;
      }
    
      .img-box {
        position: relative;
        width: 100%;
        height: 80vh;
        overflow: hidden;
        /*aspect-ratio: 1 / 1;*/
        display: flex;
      }
    
      .img-box img {
        /*注意这里需要设置右外边距和地外边距自动(auto)否则改变 上外边距或左外边距可能造成缩放效果*/
        margin: 0 auto auto 0;
      }
    
      .drop-wrapper {
        position: absolute;
        left: 0;
        top: 0;
        right: 0;
        bottom: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        /*background-color: rgba(0, 0, 0, 0.5);*/
        pointer-events: none;
        overflow: hidden;
        border: none;
      }
    
      #drop-canvas {
        box-shadow: 0 0 0 500px rgba(0, 0, 0, 0.5);
        pointer-events: none;
      }
    
      </style>
    </head>
    <body>
    <div style="overflow: hidden;">
      <input type="file" name="file" onchange="fileChange(this)">
      <button onclick="toShow()">预览裁剪的图片</button>
      <br>
      <div class="wrapper">
        <div class="img-box">
          <img src="" id="img">
        </div>
        <div class="drop-wrapper">
          <canvas id="drop-canvas" width="300" height="300"></canvas>
        </div>
      </div>
    
    </div>
    
    <script type="text/javascript">
    const image = document.getElementById('img');
    const dropWrapper = document.querySelector(".drop-wrapper");
    const dropCanvas = document.getElementById("drop-canvas");
    const dropCtx = dropCanvas.getContext("2d");
    let isInit = false;
    let ratio = 1.0, x = 0, y = 0;
    const dropLeft = (dropWrapper.clientWidth - 300) / 2;
    const dropTop = (dropWrapper.clientHeight - 300) / 2;
    
    function fileChange(e) {
    isInit = false; console.log(e.files[
    0]); const file = e.files[0]; const reader = new FileReader(); reader.onload = function () { image.onload = () => { initImageHandle(); }; image.src = reader.result; }; reader.readAsDataURL(file); e.value = ''; } // 初始化图片后的一些操作 // 图片居中 function initImageHandle() { if (image.naturalWidth < 100 || image.naturalHeight < 100) { alert('选择的图片最小宽高为100*100'); image.src = ''; return; } initRatioHandle(); // 修正边界问题 handleZoomBoundary(); handleCenter(); } // 初始处理合理的缩放比例 function initRatioHandle() { const originalWidth = image.naturalWidth; const originalHeight = image.naturalHeight; if (originalWidth < 300 || originalHeight < 300) { if (originalWidth > originalHeight) { ratio = originalHeight / 300; } else { ratio = originalWidth / 300; } } if (originalWidth > 600 && originalHeight > 600) { if (originalWidth > originalHeight) { ratio = 600 / originalHeight; } else { ratio = 600 / originalWidth; } } isInit = true; } // 缩小 function zoomOut(decr = 0.01) { if(isInit === false) return; let _ratio = ratio - decr; if (_ratio <= 0) _ratio = 0.01; let width = image.naturalWidth * _ratio; let height = image.naturalHeight * _ratio; if (width < 200 || height < 200) { if (width > height) { height = 200; _ratio = 200 / image.naturalHeight; width = image.naturalWidth * _ratio; } else { width = 200; _ratio = 200 / image.naturalWidth; height = image.naturalHeight * _ratio; } } const x = image.naturalWidth * (ratio - _ratio) / 2; const y = image.naturalHeight * (ratio - _ratio) / 2; ratio = _ratio; image.style.width = width + 'px'; image.style.height = height + 'px'; marginLeft = marginLeft + x; marginTop = marginTop + y; image.style.marginLeft = marginLeft + 'px'; image.style.marginTop = marginTop + 'px'; } // 放大 function zoomIn(incr = 0.01) { if(isInit === false) return; let _ratio = ratio + incr; if (image.naturalWidth < 200 || image.naturalHeight < 200) { if (_ratio > 3) _ratio = 3; } else { if (_ratio > 2) _ratio = 2; } const width = image.naturalWidth * _ratio; const height = image.naturalHeight * _ratio; const x = image.naturalWidth * (ratio - _ratio) / 2; const y = image.naturalHeight * (ratio - _ratio) / 2; ratio = _ratio; image.style.width = width + 'px'; image.style.height = height + 'px'; marginLeft = marginLeft + x; marginTop = marginTop + y; image.style.marginLeft = marginLeft + 'px'; image.style.marginTop = marginTop + 'px'; } let marginLeft = 0, marginTop = 0; function touchMove(x, y) { if(isInit === false) return; marginLeft = marginLeft + x; marginTop = marginTop + y; image.style.marginLeft = marginLeft + 'px'; image.style.marginTop = marginTop + 'px'; } function toShow() { if(isInit === false) return; // canvas.toDataURL() 返回的是图片的base64字符串 // document.getElementById("img").src = dropCanvas.toDataURL(); // dropCanvas.toBlob((result) => { // // canvas 可以直接转换为blob // const file = blobToFile(result, "裁剪的图片"); // console.log(file); // }); dropCtx.clearRect(0, 0, 300, 300); // 这里可以使用canvas 替换 img 展示图片 缩放和移动时 使用context.drawImage 重绘即可 // 这里获取截图时 直接从这个canvas 截取即可 就不需要计算比例改变的问题了 // 注意这里是放到img标签中,缩放的是img这个壳子 // image 图像本身并没有变化 所以这里裁剪图片时要把坐标和宽高转换为缩放比例为1时的对应比例 // 比如 图像原始宽高 1000 * 1000 图片盒子宽高为 500*500 截取大小为300*300 截取图片中间位置 // 图像宽高 1000 * 1000 截取宽高为 300*300 x=350 y=350 ratio=1.0 marginLeft=marginTop=-250 dropLeft=dropTop=100 // 图像宽高为 500 * 500 截取宽高为 600*600 x=200 y=200 ratio=0.5 marginLeft=marginTop=0 dropLeft=dropTop=100 // 图像宽高为 1500 * 1500 截取宽高为 200*200 x=400 y=400 ratio=1.5 marginLeft=marginTop=-300 dropLeft=dropTop=100 // 盒子和裁剪框大小不用缩放 dropLeft,dropTop 初始化后大小是固定的 // 图像距离盒子顶部和底部的距离 我们直接获取即可 // 需要裁剪的坐标和宽高 计算如下 // 图片放大 需要截取的宽高越小 x和y坐标值也越小 // 感觉有点反人类,一般情况下可以使用双canvas 缩放时计算好坐标和宽高 使用drawImage重绘即可 最后我们截图时宽高不需要计算 一直都是300*300 不会为ratio改变而改变 // 坐标值直接计算即可 也不需要考虑ratio是多少 const x = (dropLeft - marginLeft) / ratio; const y = (dropTop - marginTop) / ratio; const width = 300 / ratio; const height = 300 / ratio; // drawImage 中间四个参数 处理裁剪问题 最后四个参数 图片的在canvas上的位置和宽高 dropCtx.drawImage(image, x, y, width, height, 0, 0, 300, 300); const blob = dataURLToBlob(dropCanvas.toDataURL()); const file = blobToFile(blob, "裁剪的图片"); console.log(file); } // DataURL 转 Blob function dataURLToBlob(dataUrl) { const arr = dataUrl.split(","); // base64 图片 开始位置 data:image/png;base64, 在`:`和`;`之间是文件的mimeType(也叫contenType) // match 匹配的值是一个数组 第一个是正则匹配的值 第二个是小括号内匹配的值 // 逗号之前是编码信息 逗号之后才是图片base64编码后的串 const mime = arr[0].match(/:(.*?);/)[1]; const bytes = atob(arr[1]); // 使用ArrayBuffer 说是可以处理 ascii 小于0的情况 const ab = new ArrayBuffer(bytes.length); const ia = new Uint8Array(ab); for (let i = 0; i < bytes.length; i++) { ia[i] = bytes.charCodeAt(i); } return new Blob([ab], {type: mime}); } // Blob 转文件 function blobToFile(blob, fileName) { return new File([blob], fileName, {type: blob.type}); } // 处理缩放时边界问题 // 首先要判断宽高 必须全部大于裁剪框宽高 // 然后在移动边界问题 function handleZoomBoundary() { if(isInit === false) return; if (ratio < 0.05) ratio = 0.05; if (ratio > 2) ratio = 2; let width = image.naturalWidth * ratio; let height = image.naturalHeight * ratio; if (width < 300 || height < 300) { if (width > height) { height = 300; ratio = 300 / image.naturalHeight; width = image.naturalWidth * ratio; } else { width = 300; ratio = 300 / image.naturalWidth; height = image.naturalHeight * ratio; } } image.style.width = width + 'px'; image.style.height = height + 'px'; } // 处理移动时的边界问题 function handleMoveBoundary() { if(isInit === false) return; const x = (dropWrapper.clientWidth - 300) / 2; const y = (dropWrapper.clientHeight - 300) / 2; const width = image.clientWidth; const height = image.clientHeight; if (marginLeft > x) { marginLeft = x; } if (marginTop > y) { marginTop = y; } if (marginLeft < 300 - width + x) { marginLeft = 300 - width + x; } if (marginTop < 300 - height + y) { marginTop = 300 - height + y; } console.log(width, height, marginLeft, marginTop); image.style.marginLeft = marginLeft + 'px'; image.style.marginTop = marginTop + 'px'; } // 处理居中 function handleCenter() { if(isInit === false) return; const width = image.clientWidth; const height = image.clientHeight; marginLeft = (300 - width) / 2 + dropLeft; marginTop = (300 - height) / 2 + dropTop; image.style.marginLeft = marginLeft + 'px'; image.style.marginTop = marginTop + 'px'; } let isTouching = false; let fingerNum = 0; // 几个手指触摸屏幕 const clientXs = []; const clientYs = []; document.querySelector(".wrapper").addEventListener('touchstart', (e) => { if(isInit === false) return; // e.stopPropagation(); e.preventDefault(); fingerNum = e.touches.length; for (let i = 0; i < fingerNum; i++) { clientXs[i] = e.touches[i].clientX; clientYs[i] = e.touches[i].clientY; } isTouching = true; console.log("开启", e.touches.length); }); document.querySelector(".wrapper").addEventListener('touchmove', (e) => { if(isInit === false) return; // e.stopPropagation(); e.preventDefault(); if (isTouching) { fingerNum = e.touches.length; if (fingerNum === 1) { // 一个手指表示需要移动图片的位置 const cx = e.touches[0].clientX; const cy = e.touches[0].clientY; const x = cx - clientXs[0]; const y = cy - clientYs[0]; touchMove(x, y); clientXs[0] = cx; clientYs[0] = cy; } else if (fingerNum === 2) { // 两个手指表示需要缩放图片 const cx1 = e.touches[0].clientX; const cy1 = e.touches[0].clientY; const cx2 = e.touches[1].clientX; const cy2 = e.touches[1].clientY; const diffX1 = cx1 - clientXs[0]; const diffX2 = cx2 - clientXs[1]; const diffY1 = cy1 - clientYs[0]; const diffY2 = cy2 - clientYs[1]; const pxRatio = 0.001; if (cx1 > cx2) { // sqrt 平方根 // pow 平方 const len = Math.sqrt(Math.pow(diffX1, 2) + Math.pow(diffY1, 2)) + Math.sqrt(Math.pow(diffX2, 2) + Math.pow(diffY2, 2)); if (diffX1 > 0 || diffX2 < 0) { // 放大 zoomIn(len * pxRatio); } if (diffX1 < 0 || diffX2 > 0) { // 缩小 zoomOut(len * pxRatio); } } else { const len = Math.sqrt(Math.pow(diffX1, 2) + Math.pow(diffY1, 2)) + Math.sqrt(Math.pow(diffX2, 2) + Math.pow(diffY2, 2)); if (diffX1 > 0 || diffX2 < 0) { // 缩小 zoomOut(len * pxRatio); } if (diffX1 < 0 || diffX2 > 0) { // 放大 zoomIn(len * pxRatio); } } clientXs[0] = cx1; clientYs[0] = cy1; clientXs[1] = cx2; clientYs[1] = cy2; } } }); document.querySelector(".wrapper").addEventListener('touchend', (e) => { if(isInit === false) return; // e.stopPropagation(); e.preventDefault(); // 触摸结束时获取不到结束点 这里根据图片最后的位置和宽高计算图片边界问题 isTouching = false; if (fingerNum === 1) { handleMoveBoundary(); } if (fingerNum === 2) { handleZoomBoundary(); handleMoveBoundary(); } console.log("关闭"); }); </script> </body> </html>
  • 相关阅读:
    Web Service接口设计(转)
    DataTable绑定到GridView时,RowDataBound事件
    SQLiteHelper
    SQL FOR XML子句的各种用法
    公历转农历函数
    SQL里变量的声明以及常用函数举例
    Python 与 Matlab混合语言编程资料
    Iterator和Generator学习心得(二)转
    python26 调用mysql 5.1
    转:程序员能力矩阵
  • 原文地址:https://www.cnblogs.com/rchao/p/15497767.html
Copyright © 2020-2023  润新知