js canvas实现裁剪图片并下载

news2025/7/12 11:56:39

简历上给自己挖的坑,面试被拷打,早就该填了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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1593357.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

8. Spring Boot 配置文件

源码地址&#xff1a;SpringBoot_demo 本篇文章内容源码位于上述地址的com/chenshu/springboot_demo/config包下 1. 配置文件是什么 上节说到&#xff0c;Spring Boot的项目分为三个部分&#xff1a;源代码目录、资源目录、单元测试目录。 而配置文件的位置就位于资源目录res…

【竞技宝jjb.lol】LOL:T1成功击败HLE晋级MSI!

北京时间2024年4月13日,英雄联盟LCK2024春季季后赛继续进行,昨天迎来败者组决赛HLE对阵T1。本场比赛HLE率先拿下一局之后,T1连续两局在后期决策上碾压HLE拿到赛点,第四局zeus祭出上单VN在中期杀穿HLE后排,最终T1以3-1的比分击败HLE晋级春季决赛,同时也拿到了MSI的参赛资格。以下…

CSS3 常用样式

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 ✍CSS3 常用样式&#x1f48e;1 CSS3 新增选择器&#x1f339;1.1 属性选择器…

Python匿名函数4不要

当你需要完成一件小工作时&#xff0c;在本地环境中使用这个函数&#xff0c;可以让工作如此得心应手&#xff0c;它就是Lambda 函数。 Lambda 函数是 Python 中的匿名函数。有些人将它们简称为lambdas&#xff0c;它们的语法如下&#xff1a; lambda arguments: expressionl…

拥有了这24个Python接单平台,你就拥有了“钞能力”

学Python能兼职挣米吗&#xff1f;怎么挣&#xff1f; 一、Python兼职种类&#xff1a; 接私活刚学会python那会&#xff0c;就有认识的朋友介绍做一个网站的私活&#xff0c;当时接单赚了4K&#xff0c;后又自己接过开发网站后台接口、做数据处理等事情&#xff0c;都赚了一…

Python实现外观模式、桥接模式、组合模式和享元模式

今天介绍四种结构型设计模式&#xff1a;外观模式、桥接模式、组合模式和享元模式 外观模式 外观模式&#xff08;Facade Pattern&#xff09;&#xff0c;它为子系统提供一个统一的接口&#xff0c;使得子系统更加容易使用。 在Python中&#xff0c;我们可以通过定义一个外…

记录-若依前端集成markdown文档,自动生成文档目录

使用版本: vue 2.6.12 html-loader 1.3.2 markdown-loader 6.0.0 github-markdown-css ^5.5.1 highlight.js 9.18.5 webpack 4.47.x 一.引入loder插件&#xff0c;html-loader和markdown-loader //安装 pnpm install html-loader --save ; pnpm install markdown-loader --sa…

Zynq学习笔记--AXI 总线概述

目录 1. AXI总线概述 1.1 主要特点 1.2 通道功能 1.3 信号概览 2. AXI Interconnect 2.1 信号说明 2.2 内部结构 3. PS-PL AXI Interface 3.1 AXI FPD/LFP/ACP 3.2 Address Editor 3.3 地址空间 3.4 AXI-DDR 4. 通过ILA观察AXI信号 4.1 AXI 读通道 1. AXI总线概述…

头歌-机器学习 第15次实验 朴素贝叶斯分类器

第1关:条件概率 任务描述 本关任务:根据本节课所学知识完成本关所设置的选择题。 相关知识 为了完成本关任务,你需要掌握条件概率。 条件概率 朴素贝叶斯分类算法是基于贝叶斯定理与特征条件独立假设的分类方法,因此想要了解朴素贝叶斯分类算法背后的算法原理,就不得…

STM32-看门狗

1、看门狗是什么&#xff1a;就是一个向下定时器&#xff0c;定时时间一到&#xff0c;就会触发一个向下的复位的中断&#xff0c;使单片机开始工作 2、作用&#xff1a;MCU微控制器构成的微型计算机系统中&#xff0c;由于微控制器的工作常常会受到来自外界电磁场的干 扰,造成…

PostgreSQL入门到实战-第二十六弹

PostgreSQL入门到实战 PostgreSQL中数据分组操作(一)官网地址PostgreSQL概述PostgreSQL中GROUP BY命令理论PostgreSQL中GROUP BY命令实战更新计划 PostgreSQL中数据分组操作(一) 如何使用PostgreSQL GROUP BY子句将行分组。 官网地址 声明: 由于操作系统, 版本更新等原因, 文…

Transformer 结构浅析

Transformer 结构浅析 文章目录 Transformer 结构浅析Transformer 网络结构编码器位置编码多头注意力层Add&NormFeed Forward 解码器带掩码的多头注意力层多头注意力层 预测 Transformer 网络结构 Transformer模型的网络结构如图&#xff0c;且transformer结构主要分为两部…

编曲知识18:EQ均衡器 齿音处理 呼吸音处理 口水音处理

EQ均衡器 齿音处理 呼吸音处理 口水音处理小鹅通-专注内容付费的技术服务商https://app8epdhy0u9502.pc.xiaoe-tech.com/live_pc/l_66151c90e4b092c1187ac699?course_id=course_2XLKtQnQx9GrQHac7OPmHD9tqbv 均衡器 均衡器 Equalizer(简称EQ) 人耳接受频率:20hz—20khz …

python基础——类型注解【变量,函数,Union】

&#x1f4dd;前言&#xff1a; 上一篇文章Python基础——面相对象的三大特征提到&#xff0c;python中的多态&#xff0c;python中&#xff0c;类型是动态的&#xff0c;这意味着我们不需要在声明变量时指定其类型。然而&#xff0c;这可能导致运行时错误&#xff0c;因为我们…

【每日一算】冒泡算法

冒泡算法就是给数据排序的意思。比如说升序&#xff0c;17&#xff0c;8&#xff0c;9&#xff0c;28&#xff0c;5.升序之后的结果就是5&#xff0c;8&#xff0c;9&#xff0c;17&#xff0c;28. 从我们的大脑思维来看&#xff0c;结果一眼就有了&#xff0c;可是机器要怎么才…

论文阅读:Polyp-PVT: Polyp Segmentation with PyramidVision Transformers

这篇论文提出了一种名为Polyp-PVT的新型息肉分割框架&#xff0c;该框架采用金字塔视觉变换器&#xff08;Pyramid Vision Transformer, PVT&#xff09;作为编码器&#xff0c;以显式提取更强大的特征。本模型中使用到的关键技术有三个&#xff1a;渐进式特征融合、通道和空间…

Linux 【进程】

什么是进程 Linux中的进程是指正在运行的程序实例。每个进程都是操作系统内部管理的独立实体&#xff0c;具有自己的地址空间、代码、数据和打开的文件等资源。进程是并发执行的基本单位&#xff0c;可以同时运行多个进程。 Linux中的进程通过创建父子关系形成一个进程树。当一…

软件测试20个基础面试题及答案

什么是软件测试&#xff1f; 答案&#xff1a;软件测试是指在预定的环境中运行程序&#xff0c;为了发现软件存在的错误、缺陷以及其他不符合要求的行为的过程。 软件测试的目的是什么&#xff1f; 答案&#xff1a;软件测试的主要目的是保证软件的质量&#xff0c;并尽可能…

Docker入门实战教程

文章目录 Docker引擎的安装Docker比vm虚拟机快 Docker常用命令帮助启动类命令镜像命令docker imagesdocker searchdocker pulldocker system dfdocker rmi 容器命令redis前台交互式启动redis后台守护式启动Nginx容器运行ubuntu交互式运行tomcat交互式运行对外暴露访问端口 Dock…

头歌-机器学习实验 第8次实验 决策树

第1关&#xff1a;什么是决策树 任务描述 本关任务&#xff1a;根据本节课所学知识完成本关所设置的选择题。 相关知识 为了完成本关任务&#xff0c;你需要掌握决策树的相关基础知识。 引例 在炎热的夏天&#xff0c;没有什么比冰镇后的西瓜更能令人感到心旷神怡的了。现…