canvas画图,拖动画好的椭圆边框

提示:canvas画图,拖动画好的椭圆边框

文章目录

  • 前言
  • 一、拖动画好的椭圆边框
  • 总结


前言

一、拖动画好的椭圆边框

test.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>canvas跟随鼠标移动画透明线</title><style>div,canvas,img{user-select: none;}.my_canvas,.bg_img{position: absolute;top: 50%;left: 50%;transform: translate(-50%,-50%);}.cf{content: '';display: block;overflow: hidden;clear: both;}.fl{float: left;}.fr{float: right;}.bg_img{width: 674px;height: 495px;background: #ddd;}.img_tools{position: absolute;top: 20px;left: 50%;transform: translateX(-50%);border: 1px solid #eee;border-radius: 64px;height: 64px;line-height: 64px;box-sizing: border-box;padding: 15px 20px 0;}.img_tool{height: 32px;line-height: 32px;color: #000;font-size: 14px;text-align: center;width: 80px;border: 1px solid #ddd;border-radius: 32px;margin-right: 10px;cursor: pointer;position: relative;}.img_tool_active{color: #409EFF;border: 1px solid #409EFF;}.show_history{position: absolute;bottom:0;left: 50%;transform: translateX(-50%);}.show_history>img{width: 120px;margin-right: 10px;border: 1px solid #eee;border-radius: 4px;}.canvas_text{width: 120px;height: 32px;line-height: 32px;position: absolute;top: 0;left: 0;border: 1px solid #c0c0c0;border-radius: 4px;font-size: 16px;outline: none;background: none;display: none;font-family: Arial, Helvetica, sans-serif;padding-left: 0;letter-spacing: 0;}</style>
</head>
<body><div class="bg_img"></div><canvas id="myCanvasBot" class="my_canvas" width="674" height="495"></canvas><canvas id="myCanvasTop" class="my_canvas" width="674" height="495"></canvas><div class="img_tools cf"><!-- <div class="img_tool img_tool_active fl" onclick="changeType('curve',this)">涂鸦</div> --><!-- <div class="img_tool fl" onclick="changeType('line',this)">直线</div> --><div class="img_tool fl" onclick="changeType('rect',this)">矩形</div><div class="img_tool fl" onclick="changeType('ellipse',this)">圆形</div><!-- <div class="img_tool fl" onclick="changeType('eraser',this)">橡皮擦</div> --><!-- <div class="img_tool fl" onclick="changeType('text',this)">文字</div> --><!-- <div class="img_tool fl" onclick="changeType('revoke',this)">撤销</div> --><!-- <div class="img_tool fl" onclick="changeType('restore',this)">恢复</div> --></div><input id="canvasText" autofocus class="canvas_text" type="text"><div id="showHistory" class="show_history"></div><script>const canvasWidth = 674;const canvasHeight = 495;//底层canvasconst botCan = document.getElementById('myCanvasBot');//顶层canvasconst topCan = document.getElementById('myCanvasTop');//底层画布const botCtx = botCan.getContext('2d');//顶层画布const topCtx = topCan.getContext('2d');//鼠标是否按下  是否移动let isDown = false,isMove = false;//鼠标是否在canvas上抬起let isCanUp = false;//需要画图的轨迹let drawPoints = [];//起始点x,ylet startPoint = {x:0,y:0};//图片历史let historyList = [];//空历史historyList.push(new Image())//当前绘画历史indexlet historyIndex = -1;//icon历史// let partHistory = [];//操作类型let drawType = 'rect';//画线宽度const lineWidth = 10;//文字大小const fontSize = 16;//画线颜色let strokeStyle = 'rgba(255,0,0,0.6)';//path2D图形列表let pathList = [];//path2D单个图形let pathObj = null;//path2D的唯一标识let pathId = 0;//当前被激活的path2Dlet activePath = null;//是否为拖拽行为let isDrag = false;//拖拽是否移动let isDragMove = false;//是否为改变尺寸行为isResize = false;//改变尺寸点list let pointsList = [];//拖拽修改尺寸的点let activePoint = null;//文字输入框initconst canvasText = document.getElementById('canvasText');canvasText.style.display = 'none';canvasText.style.lineHeight = '32px';canvasText.style.height = '32px';canvasText.style.display = 'none';canvasText.style.color = 'none';canvasText.addEventListener('blur',()=>{topCtx.font = fontSize + 'px Arial, Helvetica, sans-serif';let h = parseFloat(canvasText.style.height);topCtx.fillText(canvasText.value, startPoint.x+1, startPoint.y+h/2+fontSize/2-1);canvasText.style.display = 'none';canvasText.value = '';topToBot();})//起始点x,ylet textPoint = {x:0,y:0};//鼠标按下const mousedown = (e)=>{isDown = true;let x = (e||window.event).offsetX;let y = (e||window.event).offsetY;if(canvasText.style.display == 'none')startPoint = {x,y};//检测是否点击到图形activePath = isPointInPath(x,y);console.log(activePath,'?????????????')if(activePath){isDrag = true;switch (drawType){case 'rect':makePathActive();break;case 'ellipse':makePathActive();break;}return;}if(drawType == 'text'){textPoint = {x:x+topCan.offsetLeft-canvasWidth/2,y:y+topCan.offsetTop-canvasHeight/2};// canvasText.style.height = 32 + 'px';canvasText.style.top = textPoint.y+'px';canvasText.style.left = textPoint.x+'px';canvasText.style.display = 'block';canvasText.style.fontSize = fontSize + 'px';canvasText.style.color = strokeStyle;setTimeout(()=>{canvasText.focus();},100)}if(drawType == 'curve'){drawPoints = [];drawPoints.push([{x,y}]);}topCtx.strokeStyle = topCtx.fillStyle = botCtx.strokeStyle = botCtx.fillStyle = strokeStyle;topCtx.lineWidth = botCtx.lineWidth = lineWidth;topCtx.lineCap = topCtx.lineJoin = botCtx.lineCap = botCtx.lineJoin = 'round';// topCtx.beginPath();// topCtx.moveTo(x,y);}//鼠标移动const mousemove = (e)=>{let x = (e||window.event).offsetX;let y = (e||window.event).offsetY;if(isDown){isMove = true;if(isDrag){isDragMove = true;switch(drawType){case 'curve':// drawCurve(x,y);break;case 'line':// drawLine(x,y);break;case 'eraser':// drawEraser(x,y);break;case 'rect':// xy 为当前point的坐标// startPoint为点击的矩形上点   查看当前point.x点距离startPoint.x移动了多少  point.y点距离startPoint.y移动了多少console.log(activePath.x + (x - startPoint.x),activePath.y + (y - startPoint.y),activePath.width,activePath.height)drawRect(activePath.x + (x - startPoint.x),activePath.y + (y - startPoint.y),activePath.width,activePath.height);break;case 'ellipse':// drawEllipse(x,y);drawEllipse(activePath.x + (x - startPoint.x),activePath.y + (y - startPoint.y),activePath.radiusX,activePath.radiusY);break;}return;}switch(drawType){case 'curve':drawCurve(x,y);break;case 'line':drawLine(x,y);break;case 'eraser':drawEraser(x,y);break;case 'rect':// drawRect(x,y);drawRect(startPoint.x, startPoint.y, x-startPoint.x, y - startPoint.y);break;case 'ellipse':drawEllipse((x+startPoint.x)/2, (y+startPoint.y)/2, Math.abs((x-startPoint.x)/2), Math.abs((y-startPoint.y)/2),0,0, Math.PI*2,true);break;}}}//鼠标抬起const mouseup = (e)=>{isCanUp = true;if(isDown){isDown = false// topCan内容画到botCan上if(isDrag){isDrag = false;activePath = null;if(isDragMove){isDragMove = false;pathList.pop();}else{pathObj = pathList.pop();}topToBot();return}if(drawType!='text')topToBot();}}//topCan内容画到botCan上const topToBot = ()=>{if(pathObj){pathObj.id = pathId++;pathList.push(pathObj);topCtx.clearRect(0,0,canvasWidth,canvasHeight);if(isCanUp)isCanUp=false;botCtx[pathObj.type](pathObj.path);pathObj = null;}//把topCan画布生成图片// let img = new Image();// img.src = topCan.toDataURL('image/png');// img.onload = ()=>{//     // partHistory.push(img);//     //添加到botCtx画布//     botCtx.drawImage(img,0,0);//     let historyImg = new Image();//     historyImg.src = botCan.toDataURL('image/png');//     historyImg.onload = ()=>{//         //添加到历史记录//         historyList.push(historyImg);//         historyIndex = historyList.length - 1;//         let ele = document.getElementById('showHistory');//         let html='';//         for(let i=0;i<historyList.length;i++){//             if(historyList[i].src)html += `<img src="${historyList[i].src}" alt="">`//         }//         ele.innerHTML = html;//     }//     //清除topCtx画布//     topCtx.clearRect(0,0,canvasWidth,canvasHeight);//     //botCan画完之后,重置canvas的mouseup isCanUp//     if(isCanUp)isCanUp=false;// }drawPoints = [];isDown = false;isMove = false;}//判断是否点击到图形const isPointInPath = (x,y)=>{let PointInPath = null;for(let i=0;i<pathList.length;i++){let path = pathList[i];// if(botCtx.isPointInPath(path.path,x,y)){if(botCtx.isPointInStroke(path.path,x,y)){PointInPath = path;break;}}return PointInPath;}//激活rect图形轮廓const makePathActive = ()=>{botCtx.clearRect(0,0,canvasWidth,canvasHeight);let arr = [];for(let i=0;i<pathList.length;i++){let path = pathList[i] if(activePath.id != path.id){botCtx[path.type](path.path);arr.push(path);}else{topCtx[path.type](path.path);//生成点,可拖拽// let point = }   }arr.push(activePath);pathList = arr;}//画椭圆形const drawEllipse = (x,y,radiusX,radiusY)=>{//清除topCtx画布topCtx.clearRect(0,0,canvasWidth,canvasHeight);topCtx.beginPath();let path = new Path2D();// 椭圆path.ellipse(x,y,radiusX,radiusY,0,0, Math.PI*2,true);// topCtx.ellipse((x+startPoint.x)/2, (y+startPoint.y)/2, Math.abs((x-startPoint.x)/2), Math.abs((y-startPoint.y)/2),0,0, Math.PI*2,true);topCtx.stroke(path);pathObj = {shape:'ellipse',type:'stroke',path,x, y, radiusX, radiusY};}//画矩形const drawRect = (x,y,width,height)=>{//清除topCtx画布topCtx.clearRect(0,0,canvasWidth,canvasHeight);topCtx.beginPath();let path = new Path2D();// 矩形path.rect(x,y,width,height);topCtx.stroke(path);pathObj = {shape:'rect',type:'stroke',path,x, y, width, height};}//橡皮擦const drawEraser = (x,y)=>{//橡皮擦圆形半径const radius = lineWidth/2;botCtx.beginPath(); for(let i=0;i<radius*2;i++){//勾股定理高hlet h = Math.abs( radius - i); //i>radius h = i-radius; i<radius  h = radius - i//勾股定理llet l = Math.sqrt(radius*radius -h*h); //矩形高度let rectHeight = 1;//矩形宽度let rectWidth = 2*l;//矩形Xlet rectX = x-l;//矩形Ylet rectY = y-radius + i;botCtx.clearRect(rectX, rectY, rectWidth, rectHeight);}}//画透明度直线const drawLine = (x,y)=>{if(!isDown)return;//清空当前画布内容topCtx.clearRect(0,0,canvasWidth,canvasHeight);//必须每次都beginPath  不然会卡topCtx.beginPath();topCtx.moveTo(startPoint.x,startPoint.y);topCtx.lineTo(x,y);topCtx.stroke();}//画带透明度涂鸦const drawCurve = (x,y)=>{drawPoints.push({x,y});//清空当前画布内容topCtx.clearRect(0,0,canvasWidth,canvasHeight);//必须每次都beginPath  不然会卡topCtx.beginPath();topCtx.moveTo(drawPoints[0].x,drawPoints[0].y);for(let i=1;i<drawPoints.length;i++){topCtx.lineTo(drawPoints[i].x,drawPoints[i].y);}topCtx.stroke();}//切换操作const changeType = (type,that)=>{// if(drawType == type) return;let tools = document.getElementsByClassName('img_tool');for(let i=0;i<tools.length;i++){let ele = tools[i];if(ele.classList.contains('img_tool_active'))ele.classList.remove('img_tool_active'); //ele.removeClassName('img_tool_active');}that.classList.add('img_tool_active');drawType = type;//撤销if(drawType == 'revoke'){if(historyIndex>0){historyIndex--;drawImage(historyList[historyIndex]);}//恢复}else if(drawType == 'restore'){if(historyIndex<historyList.length - 1){historyIndex++;drawImage(historyList[historyIndex]);}}}const drawImage = (img)=>{botCtx.clearRect(0,0,canvasWidth,canvasHeight);botCtx.drawImage(img,0,0);}//canvas添加鼠标事件topCan.addEventListener('mousedown',mousedown);topCan.addEventListener('mousemove',mousemove);topCan.addEventListener('mouseup',mouseup);//全局添加鼠标抬起事件document.addEventListener('mouseup',(e)=>{let x = (e||window.event).offsetX;let y = (e||window.event).offsetY;let classList = (e.target || {}).classList || [];if(classList.contains('img_tool'))return;if(!isCanUp){if(drawType == 'line'){let clientX = topCan.getBoundingClientRect().x;let clientY = topCan.getBoundingClientRect().y;drawLine(x-clientX,y-clientY);}// topCan内容画到botCan上topToBot();}});//全局添加鼠标移动事件document.addEventListener('mousemove',(e)=>{if(isMove)return isMove = false;let x = (e||window.event).offsetX;let y = (e||window.event).offsetY;if(drawType == 'line'){let clientX = topCan.getBoundingClientRect().x;let clientY = topCan.getBoundingClientRect().y;drawLine(x-clientX,y-clientY);}});</script>
</body>
</html>

请添加图片描述

总结

踩坑路漫漫长@~@

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

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

相关文章

【慧天HTWATER】可以兼容主流GIS(shape、geodatabase、raster等)数据格式吗

​慧天[HTWATER]软件简介 针对城市排水系统基础设施数据管理的需求&#xff0c;以及水文、水力及水质模拟对数据的需求&#xff0c;实现了以数据库方式对相应数据的存储。可以对分流制排水系统及合流制排水系统进行地表水文、管网水力、水质过程的模拟计算。可以对城市低影响开…

新能源充电桩站场AI视频智能分析烟火检测方案及技术特点分析

新能源汽车充电起火的原因多种多样&#xff0c;涉及技术、设备、操作等多个方面。从技术层面来看&#xff0c;新能源汽车的电池管理系统可能存在缺陷&#xff0c;导致电池在充电过程中出现过热、短路等问题&#xff0c;从而引发火灾。在设备方面&#xff0c;充电桩的设计和生产…

网络七层模型之网络层:理解网络通信的架构(三)

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

基于龙芯2k1000 mips架构ddr调试心得(二)

1、内存控制器概述 龙芯处理器内部集成的内存控制器的设计遵守 DDR2/3 SDRAM 的行业标准&#xff08;JESD79-2 和 JESD79-3&#xff09;。在龙芯处理器中&#xff0c;所实现的所有内存读/写操作都遵守 JESD79-2B 及 JESD79-3 的规定。龙芯处理器支持最大 4 个 CS&#xff08;由…

Linux命令--rm命令总结

1.rm命令简介 rm命令是 Linux 和其他类 Unix 系统中用于删除文件或目录的命令。对于链接文件&#xff0c;只是删除了链接&#xff0c;原有文件均保持不变。 使用 rm 命令时必须小心&#xff0c;因为它会永久性地删除文件或目录&#xff0c;并且不会将其移动到回收站或提供撤销…

Elasticsearch 向量搜索

目标记录 ["你好&#xff0c;我的爱人","你好&#xff0c;我的爱妻","你好&#xff0c;我的病人","世界真美丽"] 搜索词 爱人 预期返回 ["你好&#xff0c;我的爱人","你好&#xff0c;我的爱妻"] 示例代码…

RFID智慧书柜:全民阅读新趋势,24小时借书不打烊。

近年来&#xff0c;促进全民阅读已经是一种趋势。“书香校园”“书香社区”“文化企业建设”等口号不断被提出&#xff0c;国家也在不断鼓励并提倡营造全民阅读的氛围。在这一趋势下&#xff0c;RFID智慧书柜的出现无疑为众多社区、校园、企业的首选对象。 RFID智慧书柜将纸质书…

pygame用chatgpt绘制3d沿x轴旋转的

import pygame from pygame.locals import * import sys import mathpygame.init()width, height 800, 600 screen pygame.display.set_mode((width, height))vertices [(0, 100, 0), (100, 200, 0), (300, 100, 0)]angle 0 rotation_speed 2 # 可根据需要调整旋转速度 c…

HarmonyOS 应用开发之ExtensionAbility组件

ExtensionAbility组件是基于特定场景&#xff08;例如服务卡片、输入法等&#xff09;提供的应用组件&#xff0c;以便满足更多的使用场景。 每一个具体场景对应一个 ExtensionAbilityType&#xff0c;开发者只能使用&#xff08;包括实现和访问&#xff09;系统已定义的类型。…

如何缩短职场人与人之间的差距?答案或许就隐藏在一纸社科院与杜兰大学能源管理硕士学位之中

你是否曾惊叹于同事某某的飞速进步&#xff0c;短短两年内连升三级&#xff0c;如同职场上的彗星划破夜空&#xff1f;每当看到他们晋升的喜讯在群里传播&#xff0c;你的内心是否也曾涌起一股难以名状的涟漪&#xff1f;与你一同踏入公司的伙伴&#xff0c;如今已是经理级别&a…

postgresql多选功能实现

一、背景介绍 在一所乡村小学&#xff0c;教师资源紧张&#xff0c;所以会出现一个教师身兼多职的情况&#xff0c;既是语文老师又是数学老师甚至还是体育老师&#xff0c;这个系统就是为各个班级分配老师&#xff0c;这样一个场景实现 二、代码实现及效果 后端country_teac…

基于springboot实现校园周边美食探索及分享平台系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现园周边美食探索及分享平台系统演示 摘要 美食一直是与人们日常生活息息相关的产业。传统的电话订餐或者到店消费已经不能适应市场发展的需求。随着网络的迅速崛起&#xff0c;互联网日益成为提供信息的最佳俱渠道和逐步走向传统的流通领域&#xff0c;传统的…