JQuery 在画布图像上画线并在不影响图像的情况下重置线

如何解决JQuery 在画布图像上画线并在不影响图像的情况下重置线

我遇到了这个问题,我从输入字段上传图像并将其绘制到画布中。我有事件侦听器(mousemoveclickkeydown),当用户点击图像的任何部分时,那个点就是 x1、{{1} } 坐标,当他们移动鼠标时,这就是每次更新 y1x2 的地方。

问题是总是需要一行,我用一个名为 y2 的函数来管理它,所以当 resetLine,x2 改变时,该行被重置,所以我们每次鼠标移动时,不要以一堆连续的线结束。

问题在于我们正在绘制的图像,我重置线条的方式似乎也重置了图像,我认为这是预期的,所以我必须在每次鼠标移动时重新绘制图像,我认为这不是这样做的正确方法,而且每次移动鼠标时,您都可以看到图像闪烁。 任何帮助将不胜感激。

编辑:这是我之前制作的一个示例,但是图像是使用 javascript 制作成 css 背景 url 图像的,但是它有很多问题,我不得不开始使用画布绘制背景图像: https://angle-measurement.herokuapp.com

这是我在 codepen 上做的一个类似的: https://codepen.io/Overflow0x/pen/yLaZxqm,但它们都使用 css 的背景图像,而不是画布。

y2
class APP {
  constructor() {
    this.$imageFileInput = $("#imageFileInput");
    this.$lineWeight = $("#lineWeight");
    this.$lineColor = $("#lineColor");
    this.$reset = $("#reset");
    this.$canvas = $("#canvas");
    this.canvas = document.getElementById("canvas");
    this.lineColor = "";
    this.lineWidth = 3;
    
    this.x1y1 = {
      x: 4,y: 8
    };
    
    this.x2y2 = {
      x: 0,y: 0
    };
    
    this.imageDataUrl = null;
    this.imageWidth = 0;
    this.imageHeight = 0;
    this.ctx = this.canvas.getContext("2d");
    this.y2X2MoveHandlerState = 0;
    // 0 unregistered,1,registered
  }

  setCanvasDimentions() {
    this.ctx.canvas.width = (83 / 100) * window.innerWidth;
    this.ctx.canvas.height = (60 / 100) * window.innerHeight;
  }

  registerX1Y1ClickHandler() {
    var self = this;
    self.$canvas.on('click',function(event) {
      //console.log('registerX1Y2ClickHandler event\t',event);
      console.log("click",event)
      self.x1y1 = self.getMousePos(event);
      self.log();
    });
  }

  registerSaveLineClickHandler() {
    var self = this;
    $(document).keydown(function(e) {
      console.warn(e);
      if (e.keyCode == 83) {
        // 's' button pressed...
        self.toggleY2X2MoveHandler();
      }
    });
  }

  registerY2X2MoveHandler(mode = null) {
    this.y2X2MoveHandlerState = 1;
    var self = this;
    self.$canvas.on('mousemove',function(event) {
      console.log("mousemove",event);
      self.x2y2 = self.getMousePos(event);
      self.log();
      self.resetLine();
      self.draw_line();
      self.draw_angle();
    });
  }

  toggleY2X2MoveHandler() {
    var self = this;
    if (self.y2X2MoveHandlerState == 0) {
      self.registerY2X2MoveHandler();
    } else {
      self.$canvas.off("mousemove");
      self.y2X2MoveHandlerState = 0;
    }
  }

  registerOnImageChangeHandler() {
    var self = this;
    self.$imageFileInput.on('change',function(event) {
      console.log("image change",event);
      const file = event.target.files[0];
      self.getBase64Promise(file).then(function(result) {
        self.imageDataUrl = result;
        self.drawImage(result);
        //self.ctx.canvas.style.backgroundImage = 'url(\'' + result + '\')';
      }).catch(function(error) {
        console.error("ikh\t",error)
        self.error = error;
      })
    });
  }

  registerOnLineWeightChangeHandler() {
    var self = this;
    self.$lineWeight.on('change',function(event) {
      self.lineWidth = event.target.value;
    });
  }

  registerOnColorChangeHandler() {
    var self = this;
    self.$lineColor.on('change',function(event) {
      self.lineColor = event.target.value;
    });
  }

  registerOnResetHandler() {
    var self = this;
    self.$reset.on('click',function() {
      var cords = {
        x: 0,y: 0
      };
      self.x1y1 = cords;
      self.x2y2 = cords;
      self.resetLine();
    });
  }

  angle() {
    var x1y1 = this.getObjectValuesAsArray(this.x1y1);
    var x2y2 = this.getObjectValuesAsArray(this.x2y2);
    var x1 = x1y1[0]
    var y1 = x1y1[1]
    var x2 = x2y2[0]
    var y2 = x2y2[1]
    var dy = y2 - y1;
    var dx = x2 - x1;
    var theta = Math.atan2(dy,dx); // range (-PI,PI]
    theta *= 180 / Math.PI; // rads to degs,range (-180,180]
    //if (theta < 0) theta = 360 + theta; // range [0,360)
    theta = Number(theta).toPrecision(4);
    return theta;
  }

  getBase64Promise(file) {
    return new Promise(function(resolve,reject) {
      var reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = function() {
        return resolve(reader.result);
      };
      reader.onerror = function(error) {
        return reject(error);
      };
    });
  }

  getMousePos(event) {
    var rect = this.$canvas.get(0).getBoundingClientRect();
    return {
      x: (event.clientX - rect.left) / (rect.right - rect.left) * this.ctx.canvas.width,y: (event.clientY - rect.top) / (rect.bottom - rect.top) * this.ctx.canvas.height
    };
  }

  /*
  This code (getMousePos) takes into account both changing coordinates to canvas space 
  (evt.clientX - rect.left) and scaling when canvas logical size differs 
  from its style size 
  (/ (rect.right - rect.left) * canvas.width see: Canvas width and height in HTML5).
  */

  getObjectValuesAsArray(obj) {
    return Object.keys(obj).map(function(k) {
      return obj[k]
    });
  }

  resetLine() {
    this.ctx.save();
    this.ctx.globalCompositeOperation = 'copy';
    this.ctx.strokeStyle = 'transparent';
    this.ctx.beginPath();
    this.ctx.lineTo(0,0);
    this.ctx.stroke();
    this.drawImage(this.imageDataUrl);
    this.ctx.restore();
  }

  drawImage(result) {
    var self = this;
    console.warn(result);
    var img = $('<img>',{
      src: result
    });
    img.on('load',function() {
      console.warn("jq image\t",img);
      self.imageWidth = img.get(0).naturalWidth;
      self.imageHeight = img.get(0).naturalHeight;
      self.ctx.canvas.width = img.get(0).naturalWidth;
      self.ctx.canvas.height = img.get(0).naturalHeight;
      self.ctx.clearRect(0,self.ctx.canvas.width,self.ctx.canvas.height);
      self.ctx.drawImage(img.get(0),self.ctx.canvas.height);
    });
  }

  draw_line() {
    this.ctx.strokeStyle = this.lineColor;
    this.ctx.lineWidth = this.lineWidth;
    var x1y1 = this.getObjectValuesAsArray(this.x1y1);
    var x2y2 = this.getObjectValuesAsArray(this.x2y2);
    //console.log("vectors x1y1\t",x1y1,"\t vectors x2y2",x2y2);
    this.ctx.beginPath();
    this.ctx.moveTo(x1y1[0],x1y1[1]);
    this.ctx.lineTo(x2y2[0],x2y2[1]);
    this.ctx.stroke();
    //this.ctx.strokeRect(vectors[0],vectors[1],vectors[2],vectors[3]);
  }

  draw_angle() {
    this.ctx.strokeStyle = this.lineColor;
    this.ctx.lineWidth = this.lineWidth;
    var x2y2 = this.getObjectValuesAsArray(this.x2y2);
    //console.log("vectors x1y1\t",x2y2);
    this.ctx.font = "18px DejaVuSansMono33-Regular";
    this.ctx.fillStyle = this.lineColor;
    var x2 = x2y2[0];
    var y2 = x2y2[1];
    var dx = x2;
    var dy = y2;
    var width = this.ctx.canvas.width;
    var height = this.ctx.canvas.height;

    console.warn("canvas width and height",width,height);


    this.ctx.fillText(this.angle() + "°",dx,dy);
    //this.ctx.strokeRect(vectors[0],vectors[3]);
  }

  log() {
    console.log("x2y2\t",this.x2y2);
    //console.log("ctx",this.ctx)
  }

  init() {
    this.setCanvasDimentions();
    this.registerX1Y1ClickHandler();
    this.registerY2X2MoveHandler();
    this.registerOnImageChangeHandler();
    this.registerOnColorChangeHandler();
    this.registerOnLineWeightChangeHandler();
    this.registerSaveLineClickHandler();
    this.registerOnResetHandler();
    this.log();
  }
};

$(document).ready(function() {
  var app = new APP();
  app.init();
});
@font-face {
  font-family: 'DejaVuSansMono33-Regular';
  font-style: normal;
  font-weight: normal;
  src: url("/static/DejaVuSansMono33-Regular.otf") format("truetype");
}

html,body {
  font-family: DejaVuSansMono33-Regular,"Helvetica Neue",sans-serif;
  font-size: 14px;
  background: "red";
  height: 100%;
}

canvas#canvas {
  height: 100%;
  background-size: cover;
  background-size: 100% 100%;
  display: inline;
}

#measure {
  position: absolute;
  left: -10000px;
  top: -100000px;
}

body {
  text-align: center;
  background: #f2f6f8;
}

#canvas-container {
  height: 100%;
  width: 100%;
  text-align: center;
  padding: 1rem;
  background: none;
}

input[type="color"] {
  -webkit-appearance: none;
  border: none;
  width: 32px;
  height: 32px;
}

input[type="color"]::-webkit-color-swatch-wrapper {
  padding: 0;
}

input[type="color"]::-webkit-color-swatch {
  border: none;
}

.container {
  height: 100% !important;
}

.section {
  padding-bottom: 0px !important;
  margin-bottom: 0px !important;
}

解决方法

罪魁祸首显然是这个函数:

  drawImage(result) {
    var self = this;
    console.warn(result);
    var img = $('<img>',{
      src: result
    });
    img.on('load',function() {
      console.warn("jq image\t",img);
      self.imageWidth = img.get(0).naturalWidth;
      self.imageHeight = img.get(0).naturalHeight;
      self.ctx.canvas.width = img.get(0).naturalWidth;
      self.ctx.canvas.height = img.get(0).naturalHeight;
      self.ctx.clearRect(0,self.ctx.canvas.width,self.ctx.canvas.height);
      self.ctx.drawImage(img.get(0),self.ctx.canvas.height);
    });
  }

但那里实际发生了什么?

好吧,除了 resetLine()draw_angle(),它是 mouseMove 回调处理程序的一部分,因此每次鼠标移动时都会执行。

所以:

    var img = $('<img>',{
      src: result
    });

您正在创建一个新图像并使用:

img.on('load',function() {});

您实际上是在等待它被“加载” - 每次鼠标移动。

这意味着您观察到的闪烁是由触发图像的 onload 事件以及您的画布最终获得要绘制的图像所花费的时间造成的。

修复相当简单。我建议设置一个类变量,例如

this.image=null;

并通过以下方式修改 drawImage() 函数:

  • 如果 this.imagenull,则添加 onLoad 侦听器并将局部变量 img 分配给 this.image
  • 如果 this.imagenot null,则将其用作引用而不是 img

类似于:

  drawImage(result) {
    var self = this;
    console.warn(result);
    var img = $('<img>',{
      src: result
    });

    if (this.image == null) {
      img.on('load',function() {
        console.warn("jq image\t",img);
        self.imageWidth = img.get(0).naturalWidth;
        self.imageHeight = img.get(0).naturalHeight;
        self.ctx.canvas.width = img.get(0).naturalWidth;
        self.ctx.canvas.height = img.get(0).naturalHeight;
        self.ctx.clearRect(0,self.ctx.canvas.height);
        self.ctx.drawImage(img.get(0),self.ctx.canvas.height);
      });
      this.image = img;
    } else {
      img = this.image;
      self.imageWidth = img.get(0).naturalWidth;
      self.imageHeight = img.get(0).naturalHeight;
      self.ctx.canvas.width = img.get(0).naturalWidth;
      self.ctx.canvas.height = img.get(0).naturalHeight;
      self.ctx.clearRect(0,self.ctx.canvas.height);
    }
  }

顺便说一句 - 这绝不是最优雅的方法,它只是为了让您入门。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res