js canvas实现裁剪图片并下载

本文详细介绍了如何使用JSCanvas在网页中实现图片裁剪功能,包括上传文件、设置裁剪区域、使用getImageData和putImageData进行图像处理,以及生成下载链接的过程。
摘要由CSDN通过智能技术生成

简历上给自己挖的坑,面试被拷打,早就该填了T.T
参考:【js canvas实现图片裁剪】 https://www.bilibili.com/video/BV1QK411d7n1/?share_source=copy_web&vd_source=bf743b20b76eab11028ba2fb05f056b4

效果

在这里插入图片描述

思路

组成:

上传文件区域
----------------------------------------------
原图canvas  |   裁剪出的图片canvas  | 下载图片按钮

上传文件区域

<input> 标签,type为file

原图canvas

在上面有一个半透黑的裁剪区域,可以鼠标拖拽移动,实际上是在原图canvas上画了一个正方形,需要注意不能移出原图的区域。
使用ctx.getImageData()可以得到指定区域的ImageData对象,是一个像素值的数组对象
在这里插入图片描述
在这里插入图片描述

裁剪出的图片canvas

使用clipCvx.putImageData(imageData)可以传入刚刚裁剪得到的ImageData对象,绘制到目标位图中

下载图片按钮

clipCvs.toDataUrl()获取canvas的转成图片的dataurl格式,用fetch()请求这个url资源,因为我们是在请求一个图片,为了解析正常,我们对响应执行 Body.blob 来设置相应的 MIME 类型。然后创建一个 Object URL,赋给<a download>标签的href作为下载链接

源码

注释写得very详细了

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>canvas裁剪图片</title>
</head>

<body>
    <div style="margin-bottom: 10px">
        上传图片:<input type="file" onchange="onChange(this.files[0])">
    </div>
    <canvas id="cvs"></canvas>
    <canvas id="clipCvs"></canvas>
    <button id="download">下载图片</button>
</body>
<script>
    const cvs = document.getElementById('cvs')  // 用于显示原图的canvas
    const clipCvs = document.getElementById('clipCvs')   // 用于拖动的裁剪范围框框 canvas
    const download = document.getElementById('download')
    const ctx = cvs.getContext('2d')
    const clipCtx = clipCvs.getContext('2d')
    const img = new Image()  // 创建image节点
    let size = 150  // 正方形裁剪框框的边长
    let maxW = 400  // 用于显示原图的canvs的最大宽度
    const p = {
        left: 0,
        top: 0,
        stepX: 0,
        stepY: 0
    }
    /**
     * 上传图片
     */ 
    const onChange = (file) => {
        onInit(URL.createObjectURL(file))
    }
    // 加载图片,并初始化裁剪功能
    const onInit = (src) => {
        clipCvs.width = clipCvs.height = size   // 设置正方形的裁剪框框的宽、高
        img.src = src
        img.onload = () => {
            let width = img.width
            let height = img.height
            // 在maxW的范围内设置的width和height
            if (width > maxW) {  
                
                height = maxW / width * height
                width = maxW
            }
            cvs.width = width
            cvs.height = height
            // 让clipcvs初始在图片最中间
            render(width / 2 - size / 2, height / 2 - size / 2)
        }
    }
    
    /**
     * 渲染裁剪前canvas
     * @param left clipcvs的left定位
     * @param top clipcvs的top定位
     */
    const render = (left = 0, top = 0) => {
        ctx.clearRect(0, 0, cvs.width, cvs.height)
        // 先把图片画在canvas里,左上角在目标画布上 X 轴坐标, 左上角在目标画布上 Y 轴坐标
        ctx.drawImage(img, 0, 0, cvs.width, cvs.height)
        // 为了之后画正方形裁剪框的时候 不超出图片范围
        if (left < 0) {
            left = 0
        }
        if (left > cvs.width - size) {
            left = cvs.width - size
        }
        if (top < 0) {
            top = 0
        }
        if (top > cvs.height - size) {
            top = cvs.height - size
        }
        // getImageData -- 从(left,top)坐标,裁剪出size*size大小的图片, 返回值是一个ImageData类型的对象,是一个包含像素值的数组对象
        // clipPic 裁剪出的image显示在原图右侧
        clipPic(ctx.getImageData(left, top, size, size))
        // 绘制裁剪框,即在原来从canvas上画一个半透明的正方形
        ctx.beginPath()
        ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'
        ctx.fillRect(left, top, size, size)  
        // p.left现在为裁剪框框的x轴偏移 ,p.top 为裁剪框框的y轴偏移
        p.left = left
        p.top = top
    }
    // 裁剪图片,并显示在右侧
    const clipPic = (data) => {
        clipCtx.clearRect(0, 0, clipCvs.width, clipCvs.height)
        clipCtx.putImageData(data, 0, 0)  // putImageData -- 传入一个IamgeData对象,绘制到位图中
    }
    
    let isMoving = false

    /**
     *  监听 可拖动裁剪框框的鼠标按下事件
     */
    cvs.onmousedown = (e) => {
        // e.pageX 鼠标指针相对于整个文档的 X 轴坐标
        // e.pageY 鼠标指针相对于整个文档的 Y 轴坐标
        p.stepX = e.pageX - p.left  // 在x轴方向移动的距离
        p.stepY = e.pageY - p.top   // 在y轴方向移动的距离
        isMoving = true
    }
    /**
     * 监听 可拖动裁剪框框的鼠标松开事件
     * 如果刚刚拖动了,则重新绘制canvas
     */
    cvs.onmousemove = (e) => {
        if (isMoving) {
            render(e.pageX - p.stepX, e.pageY - p.stepY)   // 其实算出来的是 p.left + e.pageX2 - e.pageX1, p.top + e.pageY2 - e.pageY1
        }
    }
    /**
     * 监听 可拖动裁剪框框的鼠标松开事件 把move状态置为false
     */ 
    document.onmouseup = () => {
        isMoving = false
    }
    /**
     * 下载裁剪好的图像
     */ 
    download.onclick = async () => {
        // toDataURL 返回一个包含图片展示的 data URI。可以使用 type 参数指定其类型,默认为 PNG 格式。图片的分辨率为 96dpi
        // data URI 的格式一般为:data:image/png;base64,iVBORw0KNdqsdZ05vRbvTcvXIyi2YxWpxtFA8a+9FJ/2mNFteBN1MP.....
        const res = await fetch(clipCvs.toDataURL('image/png'))
        // 可以使用 blob() 从 response 中读取一个Blob对象
        const blob = await res.blob()
        const a = document.createElement('a')
        a.setAttribute('download', 'clip.png') // download属性让浏览器将URL视为下载资源,可以不填值(会自动赋值),值为文件名
        a.href = URL.createObjectURL(blob)  // 置为a标签的href
        a.click()
    }
    /**
     * 页面初始化 初始运行
     */ 
    onInit('./images/boy.jpg')
</script>
</html>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值