- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我的实现是这样的:
检测mousedown和mousemove是否为真,然后绘制点并将其保存在数组中。
在我的mousemove中,我将转换将绘制的点
我将curPath
转换为(Date,value)
,然后转换为(X and Y-axis
),以便将它们保存在画布中的实现中。
我的问题是我将如何检测点[]?这样我就可以突出显示它并拖动它。
最佳答案
更新。
这比我预期的要大。我将继续提高答案的质量。有关状态,请参见答案底部。
采摘。
最简单的方法是检查鼠标与直线上每个点的距离,并突出显示具有最接近点的直线。问题是,当您有多条线和许多点时,它会减慢速度并变得无法使用。
另一种方法是在每行上存储一些额外的信息,以帮助您审核不会被选中的行。在示例中,我为每条线创建一个边界框,然后检查鼠标是在该框内还是附近。如果是这样,那么我将在该行中进行更多搜索,检查每个线段并保持最接近鼠标的线。
一些功能要看。
Helpers.prototype.resetExtent();
Helpers.prototype.extent();
Helpers.prototype.copyOfExtent();
function refreshLine(line);
function findLineAt(x,y){
x,y
位置(或其他位置),并返回20像素内最接近的行。它首先检查边界框,如果通过则调用
Helpers.prototype.getDistToPath = function (line,x,y) {
Helpers.prototype.distPointToLine = function (x, y, x1, y1, x2, y2) {
x,y
是点,
x1,y1,x2,y2
是线。它不检查线段,而是检查无限长的线。
Helpers.lineSegPos
属性将保留线上最近点的标准化位置。如果需要的话。
findLineAt(x,y)
,在所有这些调用之后,它将返回该行(如果找到)或未定义的行。
mouse.lastButton
,以便检查鼠标何时上下移动。
pick
部分中,如果鼠标未按下,则调用
findLineAt
函数。如果找到一行(行!==未定义),则通过更改其颜色并绘制该行来突出显示该行。
mouse.button
为true而
mouse.lastButton
为false。如果鼠标附近有一条线,我将鼠标位置记录在
dragOffX
和
dragOffY
中,并将标志
dragging
设置为true。我还将画布绘制到另一个画布上以保留背景。此时,我还要检查哪个鼠标按钮按下。如果使用右键,则复制该行并将其设置为要拖动的行,或者如果使用中间按钮,我搜索所有行以在
lineArray
中找到其索引并将其删除,然后重新绘制。
dragging
标志为true
mouse.button
为true并且
lastLine
(最近的线)不是未定义的,我知道我正在拖动线。我清除画布,绘制画布的保存副本(绘制速度更快,然后再次重画所有线条,特别是如果您有100线和1000磅的点),然后重画我拖动的线。
dragOffX
和
dragOffY
的距离,并将
setTransform(1, 0 , 0, 1, mouse.x - dragOffX, mouse.y - dragOffY)
的转置部分设置为该距离。这具有将线移动拖动量的效果。我一直这样做,直到鼠标按钮向上。
dragging
标志为true,那么我必须删除该行。此时,获取距
dragOffX
dragOffY
的鼠标距离并将其添加到行中的每个点。有效地移动路线。我还更新了边界框。然后,我清除屏幕并重新绘制所有线条,这从画布上删除了线条的旧位置,并将其放置在新位置。
lineArray
中,一行具有
style, extent, id
属性,以及一个包含所有点的名为
line
的数组。
Helpers.prototype.getDistToPath(line,x,y)
。现在,它更快并且不会丢失平行于x和y轴的线。屏幕重绘已移动,以适应边框并添加更多注释。
function log(){}; // duck up the logs
customCursors ={
encoding:"url('data:image/png;base64,",
drag_small : {
center : " 25 25,",
image : "iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAACQElEQVRoQ+2azW7DIAyAYZdJW6vlVmmnvcLe/yH2CjtN6i1Tu0m9rIMsJIYChmCvCWkuqZSA/fkPQyoF83VWl5RSqJtQd8kpjnVyB4QdiA0GghhvcHuIBcYH8h9A5DAxEG4gUhgN8rzbiY/9Hs1zjpAjg0nxiEtIDUQCMwWEI+SKYfJBzorDFkvloSvAXKZTs92K9nAoXlTJYFwV9YofunyNAEWHQALjU9qETijpA2OK9CkaHLJ8NYumBrzBoMss/sK6wkyHDLRJyp6EKsxyZUc9Y5R62mzE5/GYvB+hhNFVMVV+EMZVKGeVpoYxwYHp4IUp3VhxwehwjwFdwIQUwawC84oTJgZkwaQogRfIvzcA/DCkb1m63Eu9sE4CFqQBxgty+hLi/mHocnMOVyzFf96EuHv1AkKopmlE27YW5wiuDHD6Vvo8Ds/daOlggh7pYMbBqdaEnon9zpmve9ejDwSS0f3IRBgYGqOwF2W0dysEKWCskO4dkz1vbADMF9PaQ6OF8qBECT1ndZ6pJ2eMa6upZlGg/mFunF91ncGAFtcBxIDmApPVm4WA5gCD6bCO/Qz0EFzMFrvTnLoip3TfKUbJlb+uA41c60S7cPUQS+Ip8syYm2eg9dzjoMFK/edy19KxTqI0j4o9Y5LdVXqxXwFy+zYXfHbfZ9IPKWb85QyrXlh1oqxuxTmDdduJ22sSPUgmgUBV/A8gx0OUoWX1jVhMT3leVW8WKgpcHmFtZ3whxw2iZZIWAF9IOod/rPJ+AQ3iOFgpekFcAAAAAElFTkSuQmCC')"
},
}
function setCursor (name){
if(name === undefined){
canvas.style.cursor = "default";
}
if(customCursors[name] !== undefined){
var cur = customCursors[name];
canvas.style.cursor = customCursors.encoding + cur.image + cur.center + " pointer";
}else{
canvas.style.cursor = name;
}
}
// get canvas button and creat context2D
var canvas = document.getElementById("canV");
var ctx = canvas.getContext("2d");
var but = document.getElementById("redrawAllID");
but.addEventListener("click",function(){
if(drawMode === "Pick"){
drawMode = "Draw";
but.value = "Draw Mode";
}else{
drawMode = "Pick";
but.value = "Pick Mode";
lastLine = undefined;
backGroundImage.ctx.clearRect(0,0,backGroundImage.width,backGroundImage.height);
backGroundImage.ctx.drawImage(canvas,0,0);
}
})
// Groover Bitmaps API dependency replacement
// Extracted from Groover.Bitmaps
var createImage= function(w,h){ // create a image of requier size
var image = document.createElement("canvas");
image.width = w;
image.height =h;
image.ctx = image.getContext("2d"); // tack the context onto the image
return image;
}
var backGroundImage = createImage(canvas.width,canvas.height);
if(!mouse){
// get all the mouse events
canvas.addEventListener('mousemove',mouseMoveEvent);
canvas.addEventListener('mousedown',mouseMoveEvent);
canvas.addEventListener('mouseup' ,mouseMoveEvent);
canvas.addEventListener('mouseout' ,mouseMoveEvent);
canvas.addEventListener("contextmenu", function(e){ e.preventDefault();}, false);
// helper for random colour
var mouse = { // mouse data
x:0,
y:0,
button:false,
lastButton:false, // need this to see when the mouse goes down
which:[false,false,false],
};
}
function mouseMoveEvent(event){// handle all canvas mouse events as they come in
// get new mouse positions
mouse.x = event.offsetX;
mouse.y = event.offsetY;
if(mouse.x === undefined){ // if firefox
mouse.x = event.clientX;
mouse.y = event.clientY;
}
if(event.type === "mouseout"){
mouse.button = false;
mouse.which[0] = false;
mouse.which[1] = false;
mouse.which[2] = false;
}
if(event.type === "mousedown"){ // now see if there is extra info
mouse.button = true;
mouse.which[event.which-1] = true;
}
if(event.type === "mouseup"){ // now see if there is extra info
mouse.button = false;
mouse.which[event.which-1] = false;
}
event.preventDefault();
}
// because forEach is too slow
if (Array.prototype.each === undefined) {
Object.defineProperty(Array.prototype, 'each', {
writable : false,
enumerable : false,
configurable : false,
value : function (func) {
var i,
returned;
var len = this.length;
for (i = 0; i < len; i++) {
returned = func(this[i], i);
if (returned !== undefined) {
this[i] = returned;
}
}
}
});
}
// helper functions
function Helpers(){
}
Helpers.prototype.randomColour = function(){
return "hsl("+Math.floor(Math.random()*360)+",100%,50%)";
}
Helpers.prototype.occilatingColour = function(){
var t = (new Date()).valueOf()
return "hsl("+(Math.floor(t/2)%360)+",100%,50%)";
}
// used for building up the extent of a cloud of points
Helpers.prototype.resetExtent = function(){
if(this.extentObj === undefined){ // check if the extentObj is there
this.extentObj = {}; // if not create it
}
this.extentObj.minX = Infinity;
this.extentObj.minY = Infinity;
this.extentObj.maxX = -Infinity;
this.extentObj.maxY = -Infinity;
}
Helpers.prototype.extent = function( p) { // add a point to the extent
this.extentObj.minX = Math.min(this.extentObj.minX, p.x);
this.extentObj.minY = Math.min(this.extentObj.minY, p.y);
this.extentObj.maxX = Math.max(this.extentObj.maxX, p.x);
this.extentObj.maxY = Math.max(this.extentObj.maxY, p.y);
}
Helpers.prototype.copyOfExtent = function () { // get a copy of the extent object
return {
minX : this.extentObj.minX,
minY : this.extentObj.minY,
maxX : this.extentObj.maxX,
maxY : this.extentObj.maxY,
centerX : (this.extentObj.maxX-this.extentObj.minX)/2,
centerY : (this.extentObj.maxY-this.extentObj.minY)/2,
width:this.extentObj.maxX-this.extentObj.minX,
height:this.extentObj.maxY-this.extentObj.minY,
};
}
Helpers.prototype.getID = function(){ // return a unique ID for this session
if(this.id === undefined){
this.id = 0;
}
this.id += 1;
return this.id;
}
// function to get distance of point to a line
Helpers.prototype.distPointToLine = function (x, y, x1, y1, x2, y2) {
var px = x2 - x1;
var py = y2 - y1;
var u = this.lineSegPos = Math.max(0, Math.min(1, ((x - x1) * px + (y - y1) * py) / (this.distSqr1 = (px * px + py * py))));
return Math.sqrt(Math.pow((x1 + u * px) - x, 2) + Math.pow((y1 + u * py) - y, 2));
}
// function to get the distance of a point to a set of point describing a line
Helpers.prototype.getDistToPath = function (line,x,y) {
var i,len, lineLen,dist;
len = line.length;
x1 = line[0].x;
y1 = line[0].y;
var minDist = Infinity;
for(i = 1; i < len-1; i++){
var near = false;
x2 = line[i].x;
y2 = line[i].y;
lineLen = Math.hypot(x1-x2,y1-y2);
dist = Math.hypot((x1+x2)/2-x,(y1+y2)/2-y);
minDist = Math.min(minDist,dist);
if(dist < lineLen ){
minDist = Math.min(minDist,helpers.distPointToLine(x,y,x1,y1,x2,y2));
}
if(minDist < minDistToPass){
return minDist;
}
x1 = x2;
y1 = y2;
}
return minDist;
}
var helpers = new Helpers();
// Stuff for paths and drawing
var lineArray = []; // list of paths
var lastLine; // last line drawn
var points; // current recording path
var drawing = false; // flag is mouse down and drawing
var dragging = false;
var dragOffX;
var dragOffY;
var drawMode = "Draw";
var minDistToPass = 2; // If a line is closer than this then stop search we found the winning line
// functions to redraw all recorded lines
function redrawAll(){ // style to draw in
ctx.clearRect(0,0,canvas.width,canvas.height);
lineArray.each(function(p){ // draw each one point at atime
redraw(p,p.style);
})
}
// lineDesc is a line and its description
// style is a the style to draw the line in.
// withBox if true draw bounding box [optional]
function redraw(lineDesc,style,withBox){ // draws a single line with style
var line = lineDesc.line;
var len = line.length;
var i;
ctx.beginPath(); //
ctx.strokeStyle = style.colour; // set style and colour
ctx.lineWidth = lineDesc.style.width;
ctx.lineJoin = "round";
ctx.lineCap = "round";
ctx.moveTo(line[0].x,line[0].y); // move to the first pont
for(i = 1; i < line.length; i++){ // lineto all the rest
ctx.lineTo(line[i].x,line[i].y);
};
ctx.stroke(); // stroke
if(withBox){
var w = Math.ceil(lineDesc.style.width/2); // need the lines width to expand the bounding box
ctx.lineWidth = 1;
ctx.strokeStyle = "black";
ctx.strokeRect( // draw the box around the line
lineDesc.extent.minX-w,
lineDesc.extent.minY-w,
lineDesc.extent.width+w*2,
lineDesc.extent.height+w*2
)
}
// done
}
// Finds the closest line and returns it. If no line can be found it returns undefined.
function findLineAt(x,y){
var minDist = 20; // Set the cutoff limit. Lines further than this are ignored
var minLine;
var w;
lineArray.each(function(line){ // do ech line one at a time
w = line.style.width;
if(x >= line.extent.minX-w && x <= line.extent.maxX+w && // is the point inside the bounding
y >= line.extent.minY-w && y <= line.extent.maxY+w){ // boc
var dist = helpers.getDistToPath(line.line,x,y); // if so then do a detailed distance check
if(dist < minDist){ // is the distance to the line less than any other distance found
minDist = dist; // if so remember the line
minLine = line;
}
}
dist = Math.hypot(line.extent.centerX-x,line.extent.centerY-y); // check the distance to the
if(dist<minDist){ // center of the bounding boc
minDist = dist; // use this one if bound box center if close
minLine = line;
}
});
return minLine;
}
function refreshLine(line){ // updates the line to get extend and add stuff if needed
// a good place to smooth the line if need
if(!line.isLine){
var newLine = {}; // new object
newLine.isLine = true; // flag to indicate that the line has been updated
newLine.line = line; // attach the line
newLine.id = helpers.getID(); // get a unique Id for the line
newLine.style = { // give it a style
colour:helpers.randomColour(),
width:Math.random()*4+10,
};
}else{
var newLine = line;
}
var extent = helpers.extent.bind(helpers)
helpers.resetExtent();
line.each(extent);
newLine.extent = helpers.copyOfExtent();
return newLine;
}
function update(){ // one animframe
if(drawMode === "Draw"){
if(!mouse.lastButton && mouse.button ){ // if the mouse but just down;
points = []; // create an new array
drawing = true; // flag drawinf
lineArray.push(points); // save the point array onto the pointsArray
points.push({x:mouse.x,y:mouse.y}); // add the first point
setCursor("none");
}else
if(drawing && mouse.button){ // while the mouse is down keep drawing
points.push({x:mouse.x,y:mouse.y}); // save new point
var p1 = points[points.length-2]; // get last line seg and draw it
var p2 = points[points.length-1];
ctx.lineWidth = 1;
ctx.strokeStyle = "black";
ctx.beginPath();
ctx.moveTo(p1.x,p1.y);
ctx.lineTo(p2.x,p2.y);
ctx.stroke();
}else{
if(drawing){ // if drawing and mouse up
points.push({x:mouse.x,y:mouse.y}); // add the last point
lineArray.push(points = refreshLine(lineArray.pop()))
// redraw the newly draw line
redraw(points,points.style);
drawing = false; // flag that drawing is off.
}else{
setCursor("crosshair");
}
}
}else
if(drawMode = "Pick"){
if(!dragging && !mouse.button){ // is the mouse near a line and not dragging
ctx.clearRect(0,0,canvas.width,canvas.height); // clear background
ctx.drawImage(backGroundImage,0,0); // draw copy of existing lines
var line = findLineAt(mouse.x,mouse.y); // find the line
if(line !== undefined){ // is a line is near
setCursor("drag_small"); // highlight it
lastLine = line; // remember it
// draw it hightlighted with bounding box.
redraw(lastLine,{colour:helpers.occilatingColour(),width:lastLine.width},true);
}else{
setCursor(); // not near a line so turn of cursoe
}
}else // next check if the mouse has jsut been click to start a drag.
if(lastLine !== undefined && !mouse.lastButton && mouse.button){
if(mouse.which[2]){ // Check which button. Right? then copy
var newLine = [];
lastLine.line.each(function(p){newLine.push({x:p.x,y:p.y})});
newLine = refreshLine(newLine)
newLine.style = lastLine.style;
lastLine = newLine;
lineArray.push(newLine)
}else
if(mouse.which[1]){ // Check which button. Middle? then delete
var index;
lineArray.each(function(line,i){
if(line.id === lastLine.id){
index = i;
}
})
if(index !== undefined){
lineArray.splice(index,1);
}
ctx.clearRect(0,0,canvas.width,canvas.height);
redrawAll();
lastLine = undefined;
if(lineArray.length === 0){
drawMode = "Draw";
but.value = "Draw Mode";
}
}
if(lastLine !== undefined){
dragging = true;
dragOffX = mouse.x;
dragOffY = mouse.y;
// backGroundImage.ctx.clearRect(0,0,canvas.width,canvas.height);
// backGroundImage.ctx.drawImage(canvas,0,0);
}
}else{
if(dragging && !mouse.button){ // Drop is dragging true and not mouse down
dragging = false;
var ox = mouse.x-dragOffX; // Find the drag offset
var oy = mouse.y-dragOffY;
helpers.resetExtent(); // get ready for new bounding box.
lastLine.line.each(function(p){ // move each point of the line
p.x += ox;
p.y += oy;
helpers.extent(p); // and test the bounding box
return p;
})
lastLine.extent = helpers.copyOfExtent(); // get the new boundong box
ctx.clearRect(0,0,canvas.width,canvas.height);
redrawAll();
backGroundImage.ctx.clearRect(0,0,backGroundImage.width,backGroundImage.height);
backGroundImage.ctx.drawImage(canvas,0,0);
}else
if(dragging){ // if dragging
ctx.clearRect(0,0,canvas.width,canvas.height); // clear
ctx.drawImage(backGroundImage,0,0); // draw existing lines
var ox = mouse.x-dragOffX; // get the drag offset
var oy = mouse.y-dragOffY;
ctx.setTransform(1,0,0,1,ox,oy); // translate by drag offset
redraw(lastLine,lastLine.style); //draw the dragged line
ctx.setTransform(1,0,0,1,0,0); // reset transform
}
}
}
mouse.lastButton = mouse.button; // set the last button state
window.requestAnimationFrame(update); // request a new frame
}
window.requestAnimationFrame(update)
.canC {
width:256px;
height:256px;
border:black 2px solid;
}
.info{
font-size:x-small;
}
<input type="button" id="redrawAllID" value="Click to Pick"></input>
<div class="info">Mouse down to draw.In pick mode mouse hover over line.<br> Left Button drag,middle delete, right copy.</div>
<canvas class="canC" id="canV" width=256 height=256></canvas>
关于javascript - 如何检测和移动/拖动html canvas中的自由绘制线条?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33473456/
只是想知道 Jquery Mobile 是否足够稳定以用于实时生产企业移动应用程序。 有很多 HTML5 框架,因为我们的团队使用 JQuery 已经有一段时间了,我们更愿意使用 Jquery 移动框
关闭。这个问题需要details or clarity .它目前不接受答案。 想改进这个问题吗? 通过 editing this post 添加细节并澄清问题. 关闭 3 年前。 Improve t
所以我尝试在 JavaScript 中对元素进行拖放。我使用的视频教程在这里; https://www.youtube.com/watch?v=KTlZ4Hs5h80 。我已经按照它的说明进行了编码,
无法在移动 iOS(safari 和 chrome)上自动播放以前缓存的 mp3 音频 我正在 Angular 8 中开发一个应用程序,在该应用程序的一部分中,我试图在对象数组中缓存几个传入的音频 m
Git 基于内容而不是文件,所以我目前理解以下行为,但我想知道是否有特殊选项或 hack 来检测此类事情: git init mkdir -p foo/bar echo "test" foo/a.tx
我正在寻找语义 ui 正确的类来隐藏例如移动 View 中的 DIV。在 Bootstrap 中,我们有“visible-xs”和“hidden-xs”。 但是在语义ui上我只找到了“仅移动网格” 最
我正在使用 ubuntu 和 想要移动或复制大文件。 但是当我与其他人一起使用服务器时,我不想拥有所有内存并使其他进程几乎停止。 那么有没有办法在内存使用受限的情况下移动或复制文件? 最佳答案 如果你
这些指令有什么区别?以 ARM9 处理器为例,它不应该是: ASM: mov r0, 0 C: r0 = 0; ASM: ld r0, 0 C: r0 = 0; ? 我不知道为什么要使用一个或另一个:
我有一个文件夹,其中包含一些随机命名的文件,其中包含我需要的数据。 为了使用数据,我必须将文件移动到另一个文件夹并将文件命名为“file1.xml” 每次移动和重命名文件时,它都会替换目标文件夹中以前
我经常在 IB/Storyboard 中堆叠对象,几乎不可能拖动其他对象后面的对象而不移动前面的对象。无论如何我可以移动已经选择但位于其他对象后面的对象吗?当我尝试移动它时,它总是选择顶部的对象,还是
几个月前,我看到 Safari 7 允许推送通知,它似乎是一个非常有用的工具,除了我看到的每个示例都专注于桌面浏览,而不是移动设备。 Safari 推送通知是否可以在移动设备上运行,如果没有,是否有计
我有一个简单的 View 模型,其中包含修改后的 ObservableCollection使用 SynchronizationContext.Current.Send在 UI 线程上执行对集合的更改。
关于cassandra创建的数据文件和系统文件的位置,我需要移动在“cassandra.yaml”配置文件中设置的“commitlog_directory”、“data_file_directorie
我有这个代码 $(function() { var message = 'Dont forget us'; var original; var txt1 = ' - '; $(wind
我的客户报告说他的网站有一个奇怪的问题。该网站的 URL 是 your-montenegro.me 在 基于 Android 的浏览器 上加载时,页面底部会出现一个奇怪的空白区域。以下是屏幕截图: 华
我有这个 HTML 标记: Express 300 bsf Sign Up 我需要将元素从 DOM 上的一个
我有一个可重新排序的 TableView (UITableView 实例)。尽管我已经实现了 UITableViewDataSource 方法: tableView:moveRowAtIndexPat
我的客户报告说他的网站有一个奇怪的问题。该网站的 URL 是 your-montenegro.me 在 基于 Android 的浏览器 上加载时,页面底部会出现一个奇怪的空白区域。以下是屏幕截图: 华
我需要在拖放或复制/剪切和粘贴(复制与移动)期间获取操作类型。它是一个 Swing 应用程序,并且实现了 TransferHandle。我在操作结束时需要此信息,在 importData 方法中。 对
我编写了一个具有 add 和 get 方法的 SortedIntList 类。 我调用以下四个方法: SortedIntList mySortedIntList = new SortedIntList
我是一名优秀的程序员,十分优秀!