0


web功能实例 - Canvas裁剪工具

嗯,手撸官方文档2天,发现没啥用,尤其是动画,那种计算出来的,根本想不到。因此学着学了抱着要做个东西的想法,去网上找相关案例,最终做出了这个裁剪工具。

PS :先说一下思路:

  1. 核心实现有3个canvas图层, 其中一个负责图片的预览。
  2. 另外2个叠加到一起,底层canvas负责图片的渲染; 上层的canvas负责蒙版的绘制,和选择框(挖空)区域的绘制。
  3. 我们将,移动选择框,记录的坐标和宽高,一个是同步到蒙版canvas里面,实现挖空。
  4. 第二个同步到图片渲染canvas,通过getImageData()方法,拿取选择框挖空的区域的像素数据,渲染到图片预览canvas里面,然后通过canvas的toBlob()方法将预览canvas,导出成blobUrl,实现图片下载。

针对部分核心功能进行思路讲解。

1.上传图片

FileReader.readAsDataURL() - Web API 接口参考 | MDN (mozilla.org)

在 web 应用程序中使用文件 - Web API 接口参考 | MDN (mozilla.org)

下面,我们点击upBut,从而触发upInp的change事件,从而触发文件上传。我们对文件上传的类型进行是不是图片判断。然后,将图片File ,通过FileReader对象的readAsDataURL(File)方法,将图片File转换为dataUrl,并封装成Img对象。进而绘制到图片渲染canvas里面。

除此以外涉及到的函数,下文都会讲到。

    <input type="file" id="up-inp" name="文件上传" style="display: none" />
    <button type="button" id="up-but">上传</button>

    let upBut = document.getElementById('up-but')
    let upInp = document.getElementById('up-inp')
    upBut.addEventListener('click', (e) => { // 给按钮绑定事件,点击 input type='file',从而弹出文件上传框
            upInp.click()
        })

        // 上传图片
        const updateFile =  (e) =>{
            let file = e.target.files[0]
            if (!file.type.startsWith('image')) {
                alert('只允许上传图片')
                return
            }
            const reader = new FileReader()
            reader.onload = (e) => {  // 利用fileReader将file文件转换成dataUrl
                let img1 = new Image() // 转换成img对象,进而绘画到canvas里面
                img1.src = e.target.result
                img = img1

                img1.onload = (e) => { 
                    // 读取完毕之后
                    selectCropObj = computeImage({
                        imgWidth: img.width,
                        imgHeight: img.height,
                        width: cropCardbg.width,
                        height: cropCardbg.height,
                    })
                    initImgObj = JSON.parse(JSON.stringify(selectCropObj))

                    drawImage(initImgObj)
                    drawModal()
                    drawClip()
                    drawClipDiv()
                    imgPreview()
                    cropModal.style.display = 'none'
                    clip.style.display = 'block'
                }
            }
            reader.readAsDataURL(file)
        }

        upInp.addEventListener('change', updateFile)

2. 蒙版绘制

蒙版说白了,就是占据canvas画布全部的半透明矩形。

    // 画模版
        const drawModal =  () =>{
            ctxCardbg.clearRect(0, 0, cropCardbg.width, cropCardbg.height)
            ctxCardbg.fillStyle = 'rgba(0,0,0,0.5)'
            ctxCardbg.fillRect(0, 0, cropCardbg.width, cropCardbg.height)
        }

3.挖空

挖空:蒙版随选择框的移动要扣除的透明区域。 我们先绘制蒙版,然后用clearRect()方法清除指定区域( 随着选择框移动,对应的在蒙版canvas里面的坐标和宽高围绕的区域,清除这个区域),达到挖空的效果。

        // 挖空
        const drawClip = () => {
            ctxCardbg.clearRect(selectCropObj.x, selectCropObj.y, selectCropObj.w, selectCropObj.h)
        }

4.选择框的绘制

我们先封装一个函数,用来注册拖拽选择框(中心,和8个点)的鼠标按下、移动、抬起事件。

// 注册选择框拖拽事件
        const registerEvents = () => {
            // 注册那8个拖拽点事件
            const register = (_class) => {
                let node = document.querySelector(`.${_class}`)
                node.addEventListener('mousedown', (e) => {
                    down = true
                })
                node.addEventListener('mousemove', carMouseMove)

                node.addEventListener('mouseup', function (e) {
                    down = false
                })
            }

            register('top-center')
            register('bottom-center')
            register('left-center')
            register('right-center')
            register('bottom-right')
            register('bottom-left')
            register('top-right')
            register('top-left')

            // 注册拖拽中央移动的事件
            let clip = document.getElementById('crop-clip')
            clip.addEventListener('mousedown', (e) => {
                down = true
            })

            clip.addEventListener('mousemove', carMouseMove)

            clip.addEventListener('mouseup', (e) => {
                down = false
            })
        }

这是拖拽选择框的html 结构。我们的选择框采用html绘制,由于它和另外2个叠加的canvas,由于父元素相对定位,子绝对定位叠在一起,因此选择框的left和top值,就相当于canvas里面的x、y坐标,我们同样将选择框宽高映射到,canvas的挖空区域,从而在拖拽的时候实现实时挖空的效果。

    <div id="crop-clip" style="display: none">
                <div class="dot top-left"></div>
                <div class="dot top-right"></div>
                <div class="dot top-center"></div>
                <div class="dot bottom-left"></div>
                <div class="dot bottom-center"></div>
                <div class="dot bottom-right"></div>
                <div class="dot left-center"></div>
                <div class="dot right-center"></div>
            </div>

下面通过selectCropObj对象,记录选择框在移动期间的x,y坐标,以及选择框变化的宽高。

    // 在canvas的裁剪框尺寸和坐标
        let selectCropObj = {
            x: 0,
            y: 0,
            w: 0,
            h: 0,
        }

这是我们处理选择框移动的函数。根据拖拽的元素(选择框中央、其余8个点)的携带的class不同,从而调用不同的移动处理方法。

    // 通过拖拽事件,调用的方法(将选择框中央拖拽 ,和点拖拽通过一个函数处理)
        const carMouseMove = (e) => {
            // 两个都是false,证明我们一个没按下
            if (img === null) {
                return
            }

            if (!down) {
                return
            }

            let ele = e.target
            let { movementX, movementY } = e

            const isExistsCls = (_cls) => {
                for (let i = 0; i < ele.classList.length; i++) {
                    const val = ele.classList[i]
                    if (val === _cls) {
                        return true
                    }
                }
                return false
            }

            // 中央拖拽
            if (ele.id == 'crop-clip') {
                selectCropObj.x += movementX
                selectCropObj.y += movementY
                // 点拖拽
            } else if (isExistsCls('top-left')) {
                // 坐标和宽高的都要变
                selectCropObj.x += movementX
                selectCropObj.y += movementY
                selectCropObj.w += -movementX
                selectCropObj.h += -movementY
            } else if (isExistsCls('top-right')) {
                selectCropObj.y += movementY
                selectCropObj.w += movementX
                selectCropObj.h += -movementY
            } else if (isExistsCls('top-center')) {
                selectCropObj.y += movementY
                selectCropObj.h += -movementY
            } else if (isExistsCls('bottom-left')) {
                selectCropObj.x += movementX
                selectCropObj.y += movementY
                selectCropObj.w += -movementX
                selectCropObj.h += movementY
            } else if (isExistsCls('bottom-right')) {
                selectCropObj.w += movementX
                selectCropObj.h += movementY
            } else if (isExistsCls('bottom-center')) {
                selectCropObj.h += movementY
            } else if (isExistsCls('left-center')) {
                selectCropObj.x += movementX
                selectCropObj.w += -movementX
            } else if (isExistsCls('right-center')) {
                selectCropObj.w += movementX
            }

            drawClipDiv()
            drawModal()
            drawClip()
            imgPreview()
        }

对应选择框的具体绘制方法。

        const drawClipDiv = () => {
            let cropClip = document.getElementById('crop-clip')
            cropClip.style.width = `${selectCropObj.w}px`
            cropClip.style.height = `${selectCropObj.h}px`
            cropClip.style.left = `${selectCropObj.x}px`
            cropClip.style.top = `${selectCropObj.y}px`
        }

5.画图像

就是清除之前渲染到图片canvas里面的图像,并将新的图片绘制到上面。

    // 画图像
        const drawImage = ({ x, y, w, h }) => {
            ctxImg.clearRect(0, 0, cropImg.width, cropImg.height)
            ctxImg.drawImage(img, x, y, w, h)
        }

6.图片旋转

CanvasRenderingContext2D.rotate() - Web API 接口参考 | MDN (mozilla.org)

关于图片旋转这里,就是现将原点移动canvas画布中央(图像中央),然后定义旋转角度,然后又将原点移动回去,然后画的图像就是围绕中心旋转的。

主要是围绕图像的中心原点旋转。

大家可以自行去网上找关于rotate让图像围绕中心原点旋转的问题。因为作者也没能明白。

// 主要是这段代码:

        ctxImg.translate(cropImg.width / 2, cropImg.height / 2)
        ctxImg.rotate(angle)
        ctxImg.translate(-cropImg.width / 2, -cropImg.height / 2)
    // 向右、左转(每次向左30度、向右30度)图片
        const imgRotate = (e, t = 1) => {
            if (img == null) {
                alert('请先上传图片')
                return
            }
            if (t == 1) {
                // 向右转,逆时针
                angle += -30 * (Math.PI / 180)
            } else {
                // 向左转,顺时针
                angle += 30 * (Math.PI / 180)
            }
            ctxImg.clearRect(0, 0, cropImg.width, cropImg.height)
            ctxImg.translate(cropImg.width / 2, cropImg.height / 2)
            ctxImg.rotate(angle)
            ctxImg.translate(-cropImg.width / 2, -cropImg.height / 2)
            drawImage(initImgObj)
            imgPreview()
        }

7.图片扩大和缩小

通过initImgObj对象,记录图像渲染到图片渲染canvas的尺寸和坐标。

// 图像渲染到到canvas的坐标尺寸和坐标
        let initImgObj = {
            // 主要是为了缩放/扩大图片用
            x: 0,
            y: 0,
            w: 0,
            h: 0,
        }

通过computeImage()函数,计算出图像要在canvas画布中央实际渲染的坐标和尺寸。


        // 计算图像在canvas实际渲染的图片坐标和宽高。(居于canvas中央,尺寸小于画布尺寸)
        const computeImage = function ({ imgWidth, imgHeight, width, height, base = 1 }) {
            if (imgWidth / base < width && imgHeight / base < height) {
                return {
                    x: (width - imgWidth) / 2,
                    y: (height - imgHeight) / 2,
                    w: imgWidth / base,
                    h: imgHeight / base,
                }
            }
            return computeImage({
                imgWidth,
                imgHeight,
                width,
                height,
                base: base + 0.1,
            })
        }

通过计算 initImgObj对象的放大、缩小后的宽高,然后通过computeImage()方法计算出canvas实际渲染的图像坐标和尺寸,并渲染。


        // 缩小/扩大图片
        const imgScale = (e, t) => {
            if (img == null) {
                alert('请先上传图片')
                return
            }
            let { w, h } = initImgObj

            if (t == 1) {
                // 缩小
                w = w / 2
                h = h / 2
            } else {
                // 扩大
                w = w * 2
                h = h * 2
            }

            // 重新求出,缩小、放大之后的图片宽高和坐标。
            initImgObj = computeImage({
                imgWidth: w,
                imgHeight: h,
                width: cropImg.width,
                height: cropImg.height,
            })

            drawImage(initImgObj)
            imgPreview()
        }

8.图片预览

ImageData - Web API 接口参考 | MDN (mozilla.org)

通过选择框拖拽,记录的 selectCropObj对象的坐标和宽高信息。

通过getImageData()方法获取图片渲染canvas里面的ImageData对象,并通过putImageData()方法将这个对象渲染到图片预览canvas里面。

// 图片预览
        const imgPreview = () => {
            ctxPreview.clearRect(0, 0, cropPreview.width, cropPreview.height)
            let { x, y, w, h } = selectCropObj
            let imgData = ctxImg.getImageData(x, y, w, h)
            ctxPreview.putImageData(imgData, x, y)
        }

9.保存预览图片

HTMLCanvasElement.toBlob() - Web API 接口参考 | MDN (mozilla.org)

HTMLCanvasElement.toDataURL() - Web API 接口参考 | MDN (mozilla.org)

我们通过,canvas的toBlob()方法将canvas画布转换为blobUrl,然后就是创建一个a 元素,并将这个url封装成a的href属性,用日期作为下载的图片名字,用js模拟点击实现下载。

// 保存裁剪图片
        const saveImg = (e) => {
            if (img == null) {
                alert('请先上传图片')
                return
            }
            cropPreview.toBlob((blob) => {
                // 下载图片
                let a = document.createElement('a')
                a.href = window.URL.createObjectURL(blob)
                a.download = `${getDateStr(new Date())}.png` 
                a.dispatchEvent(new MouseEvent('click'))
            })
        }

完整代码

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>图片裁剪工具</title>
        <style>
            * {
                padding: 0;
                margin: 0;
            }

            #crop-tool {
                position: relative;
                overflow: hidden;
                width: 500px;
                height: 300px;
                margin: 100px auto;
                border: 1px solid black;
                border-radius: 15px;
            }
            #crop-img {
                z-index: -1;
                top: 0;
                left: 0;
                background: url('./img/mosaic.jpg');
            }

            #crop-cardbg {
                position: absolute;
                top: 0;
                left: 0;
            }
            #crop-modal {
                position: absolute;
                top: 0;
                left: 0;
            }
            #crop-clip {
                position: absolute;
                left: 25px;
                top: 37.1364px;
                cursor: all-scroll;
                width: 308.091px;
                height: 197.864px;
                border: 1px solid rgb(30, 158, 251);
            }

            #crop-modal {
                position: absolute;
                left: 0;
                top: 0;
                width: 500px;
                height: 300px;
                line-height: 300px;
                font-size: 30px;
                font-weight: 700;
                text-align: center;
                color: white;
                background-color: rgba(0, 0, 0, 0.5);
            }
            #crop-preview {
                border: 1px solid black;
                border-radius: 15px;
                width: 500px;
                height: 300px;
                margin: 0 700px;
            }

            #crop-but {
                width: 400px;
                margin: 0 auto;
            }
            button {
                width: 50px;
            }

            #crop-clip .dot {
                position: absolute;
                width: 20px;
                height: 20px;
                border-radius: 50%;
                background: #1e9efb;
            }

            #crop-clip .top-left {
                top: -10px;
                left: -10px;
                cursor: nwse-resize;
            }

            #crop-clip .top-right {
                top: -10px;
                right: -10px;
                cursor: nesw-resize;
            }

            #crop-clip .top-center {
                top: -10px;
                left: 50%;
                transform: translate(-50%);
                cursor: ns-resize;
            }

            #crop-clip .bottom-left {
                bottom: -10px;
                left: -10px;
                cursor: nesw-resize;
            }

            #crop-clip .bottom-center {
                bottom: -10px;
                left: 50%;
                transform: translate(-50%);
                cursor: ns-resize;
            }

            #crop-clip .bottom-right {
                bottom: -10px;
                right: -10px;
                cursor: nwse-resize;
            }

            #crop-clip .left-center {
                top: 50%;
                transform: translateY(-50%);
                left: -10px;
                cursor: ew-resize;
            }

            #crop-clip .right-center {
                top: 50%;
                transform: translateY(-50%);
                right: -10px;
                cursor: ew-resize;
            }
        </style>
        <!-- <link rel="stylesheet" href="./css/iconfont.css" /> -->
    </head>
    <body>
        <div id="crop-tool">
            <canvas id="crop-img" width="500" height="300"></canvas>
            <canvas id="crop-cardbg" width="500" height="300"></canvas>
            <div id="crop-clip" style="display: none">
                <div class="dot top-left"></div>
                <div class="dot top-right"></div>
                <div class="dot top-center"></div>
                <div class="dot bottom-left"></div>
                <div class="dot bottom-center"></div>
                <div class="dot bottom-right"></div>
                <div class="dot left-center"></div>
                <div class="dot right-center"></div>
            </div>
            <div id="crop-modal">请先上传图片</div>
        </div>
        <canvas id="crop-preview" width="500" height="300"> </canvas>
        <div id="crop-but">
            <input type="file" id="up-inp" name="文件上传" style="display: none" />
            <button type="button" id="up-but">上传</button>
            <button type="button" id="add-but">+</button>
            <button type="button" id="del-but">-</button>
            <button type="button" id="xzz-but">
                left
                <!-- <span class="iconfont icon-xiangzuoxuanzhuan"></span> -->
            </button>
            <button type="button" id="xyz-but">
                right
                <!-- <span class="iconfont icon-xiangyouxuanzhuan"></span> -->
            </button>

            <button type="button" id="save-but">截图</button>
        </div>
    </body>
    <script src="./js/Index.js"></script>
    <script>
        ///
         variable
        ///

        // 按钮组
        let upBut = document.getElementById('up-but')
        let saveBut = document.getElementById('save-but')
        let addBut = document.getElementById('add-but')
        let delBut = document.getElementById('del-but')
        let xzzBut = document.getElementById('xzz-but')
        let xyzBut = document.getElementById('xyz-but')

        let upInp = document.getElementById('up-inp')

        let cropImg = document.querySelector('#crop-img') // 背景层
        let cropCardbg = document.querySelector('#crop-cardbg') // 裁剪层
        let cropPreview = document.querySelector('#crop-preview') // 裁剪层
        let cropModal = document.querySelector('#crop-modal') // 遮罩层
        let clip = document.getElementById('crop-clip') // 选择框

        let ctxCardbg = cropCardbg.getContext('2d')
        let ctxImg = cropImg.getContext('2d')
        let ctxPreview = cropPreview.getContext('2d')

        // 在canvas的裁剪框尺寸和坐标
        let selectCropObj = {
            x: 0,
            y: 0,
            w: 0,
            h: 0,
        }

        // 一开始存储到canvas的坐标尺寸和坐标
        let initImgObj = {
            // 主要是为了缩放/扩大图片用
            x: 0,
            y: 0,
            w: 0,
            h: 0,
        }
        let img = null
        let down = false // 中心拖拽或者按钮拖拽
        let angle = 0 // 旋转角度

        ///
         method
        ///
        upBut.addEventListener('click', (e) => {
            upInp.click()
        })

        // 上传图片
        const updateFile = function (e) {
            let file = e.target.files[0]
            if (!file.type.startsWith('image')) {
                alert('只允许上传图片')
                return
            }
            const reader = new FileReader()
            reader.onload = (e) => {
                let img1 = new Image()
                img1.src = e.target.result
                img = img1

                img1.onload = (e) => {
                    // 读取完毕之后
                    selectCropObj = computeImage({
                        imgWidth: img.width,
                        imgHeight: img.height,
                        width: cropCardbg.width,
                        height: cropCardbg.height,
                    })
                    initImgObj = JSON.parse(JSON.stringify(selectCropObj))

                    drawImage(initImgObj)
                    drawModal()
                    drawClip()
                    drawClipDiv()
                    imgPreview()
                    cropModal.style.display = 'none'
                    clip.style.display = 'block'
                }
            }
            reader.readAsDataURL(file)
        }

        upInp.addEventListener('change', updateFile)

        // 保存裁剪图片
        const saveImg = (e) => {
            if (img == null) {
                alert('请先上传图片')
                return
            }
            cropPreview.toBlob((blob) => {
                let a = document.createElement('a')
                a.href = window.URL.createObjectURL(blob)
                a.download = `${getDateStr(new Date())}.png`
                a.dispatchEvent(new MouseEvent('click'))
            })
        }

        saveBut.addEventListener('click', saveImg)

        // 图片预览
        const imgPreview = () => {
            ctxPreview.clearRect(0, 0, cropPreview.width, cropPreview.height)
            let { x, y, w, h } = selectCropObj
            let imgData = ctxImg.getImageData(x, y, w, h)
            ctxPreview.putImageData(imgData, x, y)
        }

        // 缩小/扩大图片

        const imgScale = (e, t) => {
            if (img == null) {
                alert('请先上传图片')
                return
            }
            let { w, h } = initImgObj

            if (t == 1) {
                // 缩小
                w = w / 2
                h = h / 2
            } else {
                // 扩大
                w = w * 2
                h = h * 2
            }

            // 重新求出,缩小,方法的图片坐标
            initImgObj = computeImage({
                imgWidth: w,
                imgHeight: h,
                width: cropImg.width,
                height: cropImg.height,
            })

            drawImage(initImgObj)
            imgPreview()
        }

        // 向右、左转(每次向左90度、向右90度)图片
        const imgRotate = (e, t = 1) => {
            if (img == null) {
                alert('请先上传图片')
                return
            }
            if (t == 1) {
                // 向右转,逆时针
                angle += -30 * (Math.PI / 180)
            } else {
                // 向左转,顺时针
                angle += 30 * (Math.PI / 180)
            }
            ctxImg.clearRect(0, 0, cropImg.width, cropImg.height)
            ctxImg.translate(cropImg.width / 2, cropImg.height / 2)
            ctxImg.rotate(angle)
            ctxImg.translate(-cropImg.width / 2, -cropImg.height / 2)
            drawImage(initImgObj)
            imgPreview()
        }

        delBut.addEventListener('click', (e) => {
            imgScale(e, 1)
        })

        addBut.addEventListener('click', (e) => {
            imgScale(e, 2)
        })

        xzzBut.addEventListener('click', (e) => {
            imgRotate(e, 1)
        })

        xyzBut.addEventListener('click', (e) => {
            imgRotate(e, 2)
        })

        // 画模版
        const drawModal = function () {
            ctxCardbg.clearRect(0, 0, cropCardbg.width, cropCardbg.height)
            ctxCardbg.fillStyle = 'rgba(0,0,0,0.5)'
            ctxCardbg.fillRect(0, 0, cropCardbg.width, cropCardbg.height)
        }

        // 计算图片在canvas实际坐标和尺寸
        const computeImage = function ({ imgWidth, imgHeight, width, height, base = 1 }) {
            if (imgWidth / base < width && imgHeight / base < height) {
                return {
                    x: (width - imgWidth) / 2,
                    y: (height - imgHeight) / 2,
                    w: imgWidth / base,
                    h: imgHeight / base,
                }
            }
            return computeImage({
                imgWidth,
                imgHeight,
                width,
                height,
                base: base + 0.1,
            })
        }

        // 画图像
        const drawImage = function ({ x, y, w, h }) {
            ctxImg.clearRect(0, 0, cropImg.width, cropImg.height)
            ctxImg.drawImage(img, x, y, w, h)
        }

        // 挖空
        const drawClip = function () {
            ctxCardbg.save()
            ctxCardbg.clearRect(selectCropObj.x, selectCropObj.y, selectCropObj.w, selectCropObj.h)
            ctxCardbg.restore()
        }

        // 挖空时,选择框的变化
        const drawClipDiv = () => {
            let cropClip = document.getElementById('crop-clip')
            cropClip.style.width = `${selectCropObj.w}px`
            cropClip.style.height = `${selectCropObj.h}px`
            cropClip.style.left = `${selectCropObj.x}px`
            cropClip.style.top = `${selectCropObj.y}px`
        }

        // 卡片拖拽事件,调用的方法(将选择框中央拖拽 ,和点拖拽一个方法处理)
        const carMouseMove = (e) => {
            // 两个都是false,证明我们一个没按下
            if (img === null) {
                return
            }

            if (!down) {
                return
            }

            let ele = e.target
            let { movementX, movementY } = e

            const isExistsCls = (_cls) => {
                for (let i = 0; i < ele.classList.length; i++) {
                    const val = ele.classList[i]
                    if (val === _cls) {
                        return true
                    }
                }
                return false
            }

            // 中央拖拽
            if (ele.id == 'crop-clip') {
                selectCropObj.x += movementX
                selectCropObj.y += movementY
                // 点拖拽
            } else if (isExistsCls('top-left')) {
                // 坐标和宽高的都要变
                selectCropObj.x += movementX
                selectCropObj.y += movementY
                selectCropObj.w += -movementX
                selectCropObj.h += -movementY
            } else if (isExistsCls('top-right')) {
                selectCropObj.y += movementY
                selectCropObj.w += movementX
                selectCropObj.h += -movementY
            } else if (isExistsCls('top-center')) {
                selectCropObj.y += movementY
                selectCropObj.h += -movementY
            } else if (isExistsCls('bottom-left')) {
                selectCropObj.x += movementX
                selectCropObj.y += movementY
                selectCropObj.w += -movementX
                selectCropObj.h += movementY
            } else if (isExistsCls('bottom-right')) {
                selectCropObj.w += movementX
                selectCropObj.h += movementY
            } else if (isExistsCls('bottom-center')) {
                selectCropObj.h += movementY
            } else if (isExistsCls('left-center')) {
                selectCropObj.x += movementX
                selectCropObj.w += -movementX
            } else if (isExistsCls('right-center')) {
                selectCropObj.w += movementX
            }

            drawClipDiv()
            drawModal()
            drawClip()
            imgPreview()
        }

        // 注册选择框拖拽事件
        const registerEvents = () => {
            // 注册那8个拖拽点事件
            const register = (_class) => {
                let node = document.querySelector(`.${_class}`)
                node.addEventListener('mousedown', (e) => {
                    down = true
                })
                node.addEventListener('mousemove', carMouseMove)

                node.addEventListener('mouseup', function (e) {
                    down = false
                })
            }

            register('top-center')
            register('bottom-center')
            register('left-center')
            register('right-center')
            register('bottom-right')
            register('bottom-left')
            register('top-right')
            register('top-left')

            // 注册拖拽中央移动的事件
            let clip = document.getElementById('crop-clip')
            clip.addEventListener('mousedown', (e) => {
                down = true
            })

            clip.addEventListener('mousemove', carMouseMove)

            clip.addEventListener('mouseup', (e) => {
                down = false
            })
        }

        registerEvents()
    </script>
</html>
const getDateStr = (time, tag1 = '-', tag2 = ':') => {
    const date = new Date(time)

    let y = date.getFullYear()
    let M = date.getMonth() + 1
    let d = date.getDate()
    let h = date.getHours()
    let m = date.getMinutes()
    let s = date.getSeconds()

    return `${y}${tag1}${M}${tag1}${d} ${h}${tag2}${m}${tag2}${s}`
}
标签: css 前端 javascript

本文转载自: https://blog.csdn.net/Qhx20040819/article/details/135206345
版权归原作者 本郡主是喵 所有, 如有侵权,请联系我们删除。

“web功能实例 - Canvas裁剪工具”的评论:

还没有评论