canvas实现图片平移,缩放的例子

最近有个水印预览的功能,需要用到canvas 绘制,canvas用的不是很熟,配合chatAI 完成功能。
效果如下
在这里插入图片描述

代码如下

原先配置是响应式的,提出来了就不显示操作了,模拟值都写死的 界面给大家参考阅读。

<!DOCTYPE html>
<html><head><title>Canvas :平移和缩放</title>
</head><body><div style="width:580px; height:440px"><canvas id="canvas"></canvas></div><script>class PreviewImage {el = nullctx = nullimage = nullscale = 1translateX = 0translateY = 0dragging = 0drag_sx = 0drag_sy = 0ratio = window.devicePixelRatio || 1constructor(el, options = {}) {this.el = elthis.options = optionsthis.init()}init() {const { el, w, h } = thisthis.ctx = el.getContext('2d')el.width = wel.height = hthis.createImage()this.bindEvent()}update(options) {this.options = optionsthis.createImage()}bindEvent() {const { el } = thisthis.mousedownBound = this.mousedown.bind(this)this.mousemoveBound = this.mousemove.bind(this)this.mouseupBound = this.mouseup.bind(this)this.wheelBound = this.wheel.bind(this)el.addEventListener('mousedown', this.mousedownBound, false)document.addEventListener('mouseup', this.mouseupBound, false)document.addEventListener('mousemove', this.mousemoveBound, false)el.addEventListener('wheel', this.wheelBound, false)}mousedown(evt) {const { clientX, clientY } = evtthis.drag_sx = clientXthis.drag_sy = clientYthis.dragging = 1document.body.style.cursor = 'move'document.body.style.userSelect = 'none'}mouseup() {this.dragging = 0document.body.style.cursor = 'auto'document.body.style.userSelect = 'auto'}mousemove(evt) {const { clientX, clientY } = evtconst { dragging, drag_sx, drag_sy } = thisif (!dragging) returnconst dx = clientX - drag_sxconst dy = clientY - drag_sythis.drag_sx = clientXthis.drag_sy = clientYthis.translate(dx, dy)}translate(dx, dy) {const { image } = thisconst { translateX, translateY } = thisconst { width, height } = imageconst x = translateX + dxconst y = translateY + dythis.translateX = Math.min(Math.max(x, width * 0.1 - width), width - width * 0.1)this.translateY = Math.min(Math.max(y, height * 0.1 - height), height - height * 0.1)this.draw()}wheel(evt) {evt.preventDefault()const { el } = thisconst { clientX, clientY, deltaY } = evtconst x = clientX - el.offsetLeftconst y = clientY - el.offsetTopconst dampeningFactor = 0.05const minScale = 0.3const maxScale = 1.5const scale = 1.0 + dampeningFactor * (deltaY > 0 ? -1 : 1)const currentScale = Math.min(Math.max(this.scale * scale, minScale), maxScale)this.zoom(currentScale, x, y)}zoom(s, x, y) {const { translateX, translateY } = thisif (s < 1.02 && s > 0.98) s = 1const offsetX = (x - translateX) / sconst offsetY = (y - translateY) / sthis.translateX = x - offsetX * sthis.translateY = y - offsetY * sthis.scale = sthis.draw()}get w() {return this.el.parentNode?.offsetWidth || 0}get h() {return this.el.parentNode?.offsetHeight - (30 + 45) || 0}async createImage() {const { ratio, options } = thistry {const img = await this.loadImage(options.src)img.width = options.imageWidthimg.height = options.imageHeightthis.image = imgthis.draw()} catch (error) {console.error(error)}}wheel(evt) {evt.preventDefault()const { el } = thisconst { clientX, clientY, deltaY } = evtconst x = clientX - el.offsetLeftconst y = clientY - el.offsetTopconst dampeningFactor = 0.05const minScale = 0.3const maxScale = 1.5const scale = 1.0 + dampeningFactor * (deltaY > 0 ? -1 : 1)const currentScale = Math.min(Math.max(this.scale * scale, minScale), maxScale)this.zoom(currentScale, x, y)}zoom(s, x, y) {const { translateX, translateY, options, ratio } = thisif (s < 1.02 && s > 0.98) s = 1const offsetX = (x - translateX) / sconst offsetY = (y - translateY) / sthis.translateX = x - offsetX * sthis.translateY = y - offsetY * sthis.image.width = options.imageWidth * ratio * sthis.image.height = options.imageHeight * ratio * sthis.scale = sthis.draw()}draw() {const { ctx, ratio, w, h, image, translateX, translateY, scale } = thisctx.clearRect(0, 0, w, h)this.drawBackground()ctx.save()ctx.translate(translateX, translateY)const imageX = (w - image.width * scale * ratio) / 2const imageY = (h - image.height * scale * ratio) / 2ctx.drawImage(image, imageX, imageY, image.width * scale, image.height * scale)this.drawTexts()ctx.restore()this.scaling()}drawTexts() {const { ctx, ratio, image, options, scale, w, h } = thisconst { texts, textStyles } = optionsconst { fontSize, fontFamily, fontColor, textAlign = 'center', lineX, lineY, lineAngle, textBaseline = 'top' } = textStylesctx.font = `${fontSize * ratio * scale}px ${fontFamily || 'sans-serif'}`ctx.fillStyle = fontColor || '#303133'ctx.textAlign = textAlignctx.textBaseline = textBaselineconst text = this.transformText(texts)const imageX = (w - image.width * scale * ratio) / 2const imageY = (h - image.height * scale * ratio) / 2const posx = imageX + image.width * scale * ratio * (lineX / 100)const posy = imageY + image.height * scale * ratio * (lineY / 100)const angle = (lineAngle / 180) * Math.PIctx.fillText(text, posx, posy)}scaling() {const { ctx, w, scale } = thisif (scale < 1.03 && scale > 0.97) returnctx.save()ctx.font = `12px  xsans-serif`ctx.fillStyle = '#303133'ctx.textAlign = 'center'ctx.textBaseline = 'top'const text = `${(scale * 100).toFixed(0)}%`ctx.fillText(text, w - ctx.measureText(text).width + 10, 10)ctx.restore()}loadImage(url) {return new Promise((resolve, reject) => {const image = new Image()image.onload = () => {resolve(image)}image.onerror = () => {reject(new Error('无法加载图片: ' + url))}image.src = url})}drawBackground() {const { ctx, ratio, w, h } = thisconst posx = (0 / 100) * w * ratioconst posy = (0 / 100) * h * ratioconst width = (100 / 100) * w * ratioconst height = (100 / 100) * h * ratioctx.beginPath()ctx.fillStyle = '#F2F6FC'ctx.fillRect(posx, posy, width, height)}transformText(arr) {arr = Array.isArray(arr) ? arr : [arr]const keywods = {'${timestamp}': new Date().toLocaleString(),'${consumerName}': '消费者名称','${terminalIP}': '127.0.0.1',}return arr.join('-').replace(/\$\{timestamp\}|\$\{consumerName\}|\$\{terminalIP\}/g, matched => keywods[matched])}destroy() {this.el.removeEventListener('mousedown', this.mousedownBound)document.removeEventListener('mousemove', this.mousemoveBound)document.removeEventListener('mouseup', this.mouseupBound)this.el.removeEventListener('wheel', this.wheelBound)}}var dialog = {visible: false,predefine: ['#ff4500', '#ff8c00', '#ffd700', '#90ee90', '#00ced1', '#1e90ff', '#c71585', '#cd5c5c', '#000000', '#ffffff'],title: '新增',controlled_width: 800,controlled_height: 600,form: {name: '',content: ['127.0.0.1'],lineX: 50,lineY: 10,lineAngle: 0,fontSize: 25,fontColor: '#ff4500',fontFamily: '',},}var picture = {images: ['https://images.pexels.com/photos/1784914/pexels-photo-1784914.jpeg?auto=compress&cs=tinysrgb&w=1600'],active: 0,}const { controlled_width, controlled_height, form } = dialogconst { fontSize, fontColor, fontFamily, lineX, lineY, lineAngle } = formconst { images, active } = pictureconst textStyles = {fontSize,fontColor,fontFamily,lineX,lineY,lineAngle,}const options = {src: images[active],imageWidth: controlled_width,imageHeight: controlled_height,texts: dialog.form.content,textStyles,}var canvas = document.getElementById('canvas')var priviewImage = new PreviewImage(canvas, options)</script>
</body></html>

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

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

相关文章

Spring AOP 的概念及其作用

一、什么是 Spring AOP&#xff1f; 在介绍 Spring AOP 之前&#xff0c;首先要了解一下什么是 AOP &#xff1f; AOP &#xff08; Aspect Oriented Programming &#xff09;&#xff1a;面向切面编程&#xff0c;它是一种思想&#xff0c; 它是对某一类事情的集中处 理 。…

软件测试面试题——接口自动化测试怎么做?

面试过程中&#xff0c;也问了该问题&#xff0c;以下是自己的回答&#xff1a; 接口自动化测试&#xff0c;之前做过&#xff0c;第一个版本是用jmeter 做的&#xff0c;1 主要是将P0级别的功能接口梳理出来&#xff0c;根据业务流抓包获取相关接口&#xff0c;并在jmeter中跑…

【前端知识】React 基础巩固(四十三)——Effect Hook

React 基础巩固(四十三)——Effect Hook 一、Effect Hook的基本使用 Effect Hook 用来完成一些类似class中生命周期的功能。 在使用类组件时&#xff0c;不管是渲染、网路请求还是操作DOM&#xff0c;其逻辑和代码是杂糅在一起的。例如我们希望把计数器结果显示在标签上&…

8.事件对象

8.1获取事件对象 ●事件对象是什么 也是个对象&#xff0c;这个对象里有事件触发时的相关信息 例如&#xff1a;鼠标点击事件中&#xff0c;事件对象就存了鼠标点在哪个位置等信息 ●使用场景 可以判断用户按下哪个键&#xff0c;比如按下回车键可以发布新闻 可以判断鼠标点击…

【Java|golang】143. 重排链表---快慢指针

给定一个单链表 L 的头节点 head &#xff0c;单链表 L 表示为&#xff1a; L0 → L1 → … → Ln - 1 → Ln 请将其重新排列后变为&#xff1a; L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → … 不能只是单纯的改变节点内部的值&#xff0c;而是需要实际的进行节点交换。 …

CentOS7.3 安装 docker

亲测、截图 阿里云服务器 文章目录 更新源2345 启动开机自启 更新源 sudo yum update -y2 sudo yum install -y yum-utils device-mapper-persistent-data lvm23 sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo4 sudo yum …

【软件测试】性能测试工具- LoadRunner的介绍和使用

目录 1. LoadRunner是什么2. LoadRunner环境搭建3. LoadRunner三大组件4. LoadRunner脚本录制4.1 WebTous项目介绍启动WebTous项目访问WebTous项目相关配置 4.2 脚本录制新建脚本录制脚本运行脚本 4.3 脚本加强插入事务插入集合点插入检查点插入日志字符串比较 1. LoadRunner是…

【奥比中光Gemini 2L快速上门】

奥比中光Gemini 2L快速上手 目录 奥比中光Gemini 2L快速上手[TOC](目录) 一、下载配置环境1.1 官网下载SDK1.2 配置环境 二、测试2.1 在bin中运行示例2.2 配置cmake 三、CMAKE3.1 CmakeLists.txt中各设置的意义 一、下载配置环境 1.1 官网下载SDK 进入官网&#xff0c;下载名…

MySQL中锁的简介——行级锁

1.行级锁概念及分类 可通过以下语句查看意向锁和行锁的加锁情况&#xff1a; select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_locks;InnoDB的行锁是针对于索引加的锁&#xff0c;不通过索引条件检索数据&#xff0…

this is incompatible with sql_mode=only_full_group_by

查看配置 select global.sql_mode 在sql命令行中输入select sql_mode 能够看到sql_mode配置,如果有ONLY_FULL_GROUP_BY&#xff0c;则需要修改 在mysql5.7.5后&#xff0c;ONLY_FULL_GROUP_BY是默认选项&#xff0c;所以就会导致group by的问题 set sql_mode‘复制去掉ONLY_F…

[SSM]Spring6整合JUnit5与集成MyBatis3.5

目录 十七、Spring6整合JUnit5 17.1Spring对JUnit4的支持 17.2Spring对JUnit5的支持 十八、Spring6集成MyBatis3.5 18.1实现步骤 18.2具体实现 18.3spring配置文件的import 十七、Spring6整合JUnit5 17.1Spring对JUnit4的支持 准备工作&#xff1a; <dependencies&…

华为数通HCIA-网络参考模型(TCP/IP)

网络通信模式 作用&#xff1a;指导网络设备的通信&#xff1b; OSI七层模型&#xff1a; 7.应用层&#xff1a;由应用层协议&#xff08;http、FTP、Telnet.&#xff09;为应用程序产生对应的数据&#xff1b; 6.表示层&#xff1a;将应用层产生的数据转换成网络设备看得懂…

C语言文件io操作

一、fopen 在C语言中&#xff0c;操作文件之前应该先打开文件。使用<stdio.h>头文件中的fopen()函数可以打开文件&#xff0c;因为FILE也是结构体&#xff0c;我们通过返回一个文件指针就可以对文件进行操作。在用完fopen之后要记得关闭该文件流。 用法&#xff1a; F…

Linux 进程查找、杀死方案集合

一、查找进程 方式一 ps 命令&#xff1a;显示当前活动进程的快照。 # 显示所有用户的所有进程 $ ps aux# 显示所有进程的完整信息 $ ps -ef# 常用参数 -a&#xff1a;显示所有进程&#xff0c;包括其他用户的进程。 -u <用户>&#xff1a;仅显示指定用户的进程信息。 -x…

【多模态】20、OVR-CNN | 使用 caption 来实现开放词汇目标检测

文章目录 一、背景二、方法2.1 学习 视觉-语义 空间2.2 学习开放词汇目标检测 三、效果 论文&#xff1a;Open-Vocabulary Object Detection Using Captions 代码&#xff1a;https://github.com/alirezazareian/ovr-cnn 出处&#xff1a;CVPR2021 Oral 一、背景 目标检测数…

Redis系列一:介绍

介绍 The open source, in-memory data store used by millions of developers as a database, cache, streaming engine, and message broker. 相关资源 Redis 官网&#xff1a;https://redis.io/ 源码地址&#xff1a;https://github.com/redis/redis Redis 在线测试&#…

学习使用axios,绑定动态数据

目录 axios特性 案例一&#xff1a;通过axios获取笑话 案例二&#xff1a;调用城市天气api接口数据实现天气查询案例 axios特性 支持 Promise API 拦截请求和响应&#xff08;可以在请求前及响应前做某些操作&#xff0c;例如&#xff0c;在请求前想要在这个请求头中加一些…

msbuild - 对话

MSBuild是一个用于构建、部署和测试.NET应用程序的命令行工具。它是微软开发工具包&#xff08;Microsoft Build Tools&#xff09;中的一部分&#xff0c;常用于自动化构建和发布过程。 可以使用MSBuild来构建Visual Studio项目或解决方案&#xff0c;并根据需要执行各种操作…

springboot 整合tx-mybaits 实现crud操作

一 操作案例 1.1 工程结构 1.2 pom文件的配置 <!--spring boot的依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId…

华为数通HCIA-地址分类及子网划分

ip地址&#xff08;逻辑地址&#xff09; 作用&#xff1a;唯一标识一张网卡 特点&#xff1a;设备天生没有&#xff0c;需要人为配置&#xff0c;可以随时修改 格式&#xff1a;点分十进制 大小&#xff1a;32bit 组成&#xff1a;网络位主机位 网络位&#xff1a;用于标…