拖放到画布 HTML5 上
Drag and drop on a canvas HTML5
我正在尝试在代表 3 个磁盘的画布上实现拖放。
我想用鼠标更改每个质量的位置。我的主要问题是我受到这 3 个球体中每个球体的斧头长度的限制。
目前,当鼠标在画布内移动时,我已经实现了以下函数(indexMass 的值表示移动的质量:1, 2 or 3
和 t1, t2, t3
分别表示the angle of mass 1, 2, 3
):
// Happens when the mouse is moving inside the canvas
function myMove(event) {
if (isDrag) {
var x = event.offsetX;
var y = event.offsetY;
if (indexMass == 1)
{ // Update theta1 value
t1 = t1 + 0.1*Math.atan(y/x);
}
else if (indexMass == 2)
{ // Update theta2 value
t2 = t2 + 0.1*Math.atan(y/x);
}
else if (indexMass == 3)
{ // Update theta3 value
t3 = t3 + 0.1*Math.atan(y/x);
}
// Update drawing
DrawPend(canvas);
}
}
如您所见,我为每个角度做了:
t = t + 0.1*Math.atan(y/x);
跟:
var x = event.offsetX;
var y = event.offsetY;
但这种效果不是很好。一旦用鼠标(鼠标单击)选择球体,我希望光标卡在这个球体上,或者当我不再在球体上时,球体跟随鼠标坐标的"delta
"。
更新 1
@Blindman67:感谢您的帮助,您的代码片段对我来说非常复杂,我并不完全理解。但我走在正确的道路上。
我从第一个问题开始:拖动时,用鼠标保持非常靠近或在其上方旋转所选磁盘。
目前,我已经修改了我的函数myMove
(当我向下单击并移动鼠标进行拖动时调用),如下所示:
// Happens when the mouse is moving inside the canvas
function myMove(event) {
// If dragging
if (isDrag) {
// Compute dx and dy before calling DrawPend
var lastX = parseInt(event.offsetX - mx);
var lastY = parseInt(event.offsetY - my);
var dx = lastX - window['x'+indexMass];
var dy = lastY - window['y'+indexMass];
// Change angle when dragging
window['t'+indexMass] = Math.atan2(dy, dx);
// Update drawing
DrawPend(canvas);
// Highlight dragging disk
fillDisk(indexMass, 'pink');
}
}
其中indexMass
是拖动磁盘的索引,window['x'+indexMass]
,window['y'+indexMass]
是所选磁盘中心的当前坐标。
之后,我分别根据开始拖动时鼠标单击的坐标(mx, my
由getMousePos function
返回)和移动鼠标坐标计算dx, dy
。
最后,我按集合更改圆盘的角度,对于全局变量(所选圆盘的θ),即window['t'+indexMass]
:
// Change angle when dragging
window['t'+indexMass] = Math.atan2(dy, dx);
我已经用Math.atan2
接受了你的部分代码.
但是这个函数的结果并不能用鼠标拖动制作出很好的动画,我想知道这可能来自哪里。
现在,我只想在不修改轴长度的情况下实现拖动,稍后我将看到此功能的更多信息。
更新 2
我继续寻找有关用鼠标拖动选定质量的解决方案。
对于尝试合成我之前所做的,我相信以下方法很好,但这种拖动方法效果不佳:所选磁盘没有正确跟随鼠标,我不知道为什么。
在myMove function
(开始拖动时调用的函数)中,我决定:
计算鼠标坐标和所选磁盘坐标之间的 dx, dy,即:
var dx = parseInt(event.offsetX - window['x'+indexMass]); var dy = parseInt(event.offsetY - window['y'+indexMass]);
indexMass
表示所选磁盘的索引。
将所选磁盘(存储在临时变量中)的位置递增
dx, dy
tmpX, tmpY
。计算新的角度
theta
(在代码中由全局变量window['t'+indexMass]
标识)用这个新值
theta
计算选定磁盘的新位置,即例如disk1
(indexMass=1
和θ=t1
):x1= x0 +l1 * sin(t1) y1= y0 +l1 * sin(t1)
我想提请读者注意这样一个事实,即我希望用鼠标拖动而不是用鼠标修改轴的长度,这是一个约束。
以下是整个myMove function
(在拖动开始时调用):
// Happens when the mouse is moving inside the canvas
function myMove(event) {
// If dragging
if (isDrag) {
console.log('offsetX', event.offsetX);
console.log('offsetY', event.offsetY);
var dx = parseInt(event.offsetX - window['x'+indexMass]);
var dy = parseInt(event.offsetY - window['y'+indexMass]);
console.log('dx', dx);
console.log('dy', dy);
// Temp variables
var tmpX = window['x'+indexMass];
var tmpY = window['y'+indexMass];
// Increment temp positions
tmpX += dx;
tmpY += dy;
// Compute new angle for indexMass
window['t'+indexMass] = Math.atan2(tmpX, tmpY);
console.log('printf', window['t'+indexMass]);
// Compute new positions of disks
dragComputePositions();
// Update drawing
DrawPend(canvas);
// Highlight dragging disk
fillDisk(indexMass, 'pink');
}
}
您不能移动操作系统鼠标位置。您可以隐藏鼠标canvas.style.cursor = "none";
然后在画布上自己绘制鼠标,但它会落后一帧,因为当您获取鼠标坐标时,操作系统已经将鼠标放置在该位置,如果您使用 requestAnimationFrame (RAF),画布的下一个演示将在下一个显示刷新间隔。如果您不使用 RAF,您可能会也可能不会在当前显示器刷新时显示画布,但偶尔会出现闪烁和剪切。
为了解决这个问题(这是主观的),从旋转点通过球到鼠标位置画一条线,这至少会给用户一些关于正在发生的事情的反馈。
我还会在球上添加一些手柄,以便您可以更改质量(球体体积*密度)和轴的长度。调整大小光标是一个问题,因为当角度发生变化时,光标将不匹配所需移动的方向。您需要找到一个最接近正确角度或将光标渲染到画布上并使用它。
示例代码显示了我的意思。(不包括SIM卡)将鼠标移到球上移动,当鼠标移到球上时,您还会看到两个圆圈出现以改变距离和半径(质量)
/*-------------------------------------------------------------------------------------
answer code
---------------------------------------------------------------------------------------*/
var balls = [];
var startX,startY;
var mouseOverBallIndex = -1;
var mouseOverDist = false;
var mouseOverMass = false;
const DRAG_CURSOR = "move";
const MASS_CURSOR = "ew-resize";
const DIST_CURSOR = "ns-resize";
var dragging = false;
var dragStartX = 0;
var dragStartY = 0;
function addBall(dist,radius){
balls.push({
dist : dist,
radius : Math.max(10,radius),
angle : -Math.PI / 2,
x : 0,
y : 0,
mass : (4/3) * radius * radius * radius * Math.PI,
});
}
function drawBalls(){
var i = 0;
var len = balls.length;
var x,y,dist,b,minDist,index,cursor;
ctx.lineWidth = 2;
ctx.strokeStyle = "black";
ctx.fillStyle = "blue"
ctx.beginPath();
x = startX;
y = startY;
ctx.moveTo(x, y)
for(; i < len; i += 1){
b = balls[i];
x += Math.cos(b.angle) * b.dist;
y += Math.sin(b.angle) * b.dist;
ctx.lineTo(x, y);
b.x = x;
b.y = y;
}
ctx.stroke();
minDist = Infinity;
index = -1;
for(i = 0; i < len; i += 1){
b = balls[i];
ctx.beginPath();
ctx.arc(b.x, b.y, b.radius, 0, Math.PI * 2);
ctx.fill();
if(!dragging){
x = b.x - mouse.x;
y = b.y - mouse.y;
dist = Math.sqrt(x * x + y * y);
if(dist < b.radius + 5 && dist < minDist){
minDist = dist;
index = i;
}
}
}
if(!dragging){
mouseOverBallIndex = index;
if(index !== -1){
cursor = DRAG_CURSOR;
b = balls[index];
ctx.fillStyle = "Red"
ctx.beginPath();
ctx.arc(b.x, b.y, b.radius, 0, Math.PI * 2);
ctx.fill();
dx = b.x - Math.cos(b.angle) * b.radius;
dy = b.y - Math.sin(b.angle) * b.radius;
x = dx - mouse.x;
y = dy - mouse.y;
dist = Math.sqrt(x * x + y * y);
ctx.beginPath();
if(dist < 6){
ctx.strokeStyle = "Yellow"
mouseOverDist = true;
ctx.arc(dx, dy, 12, 0, Math.PI * 2);
cursor = DIST_CURSOR;
}else{
ctx.strokeStyle = "black"
mouseOverDist = false;
ctx.arc(dx, dy, 5, 0, Math.PI * 2);
}
ctx.stroke();MASS_CURSOR
dx = b.x - Math.cos(b.angle + Math.PI/2) * b.radius;
dy = b.y - Math.sin(b.angle + Math.PI/2) * b.radius;
x = dx - mouse.x;
y = dy - mouse.y;
dist = Math.sqrt(x * x + y * y);
ctx.beginPath();
if(dist < 6){
ctx.strokeStyle = "Yellow"
mouseOverMass = true;
ctx.arc(dx, dy, 12, 0, Math.PI * 2);
cursor = MASS_CURSOR;
}else{
ctx.strokeStyle = "black"
mouseOverMass = false;
ctx.arc(dx, dy, 5, 0, Math.PI * 2);
}
ctx.stroke();
canvas.style.cursor = cursor;
}else{
canvas.style.cursor = "default";
}
}else{
b = balls[mouseOverBallIndex];
ctx.fillStyle = "Yellow"
ctx.beginPath();
ctx.arc(b.x, b.y, b.radius, 0, Math.PI * 2);
ctx.fill();
}
}
function display(){ // put code in here
var x,y,b
if(balls.length === 0){
startX = canvas.width/2;
startY = canvas.height/2;
addBall((startY * 0.8) * (1/4), startY * 0.04);
addBall((startY * 0.8) * (1/3), startY * 0.04);
addBall((startY * 0.8) * (1/2), startY * 0.04);
}
ctx.setTransform(1,0,0,1,0,0); // reset transform
ctx.globalAlpha = 1; // reset alpha
ctx.clearRect(0,0,w,h);
if((mouse.buttonRaw & 1) && mouseOverBallIndex > -1){
b = balls[mouseOverBallIndex];
if(dragging === false){
dragging = true;
dragStartX = balls[mouseOverBallIndex].x;
dragStartY = balls[mouseOverBallIndex].y;
}else{
b = balls[mouseOverBallIndex];
if(mouseOverBallIndex === 0){
x = startX;
y = startY;
}else{
x = balls[mouseOverBallIndex-1].x
y = balls[mouseOverBallIndex-1].y
}
if(mouseOverDist){
var dist = Math.sqrt(Math.pow(x-mouse.x,2)+Math.pow(y-mouse.y,2));
b.dist = dist + b.radius;
}else
if(mouseOverMass){
var dist = Math.sqrt(Math.pow(dragStartX-mouse.x,2)+Math.pow(dragStartY-mouse.y,2));
b.radius = Math.max(10,dist);
b.mass = dist * dist * dist * (4/3) * Math.PI;
}else{
b.angle = Math.atan2(mouse.y - y, mouse.x - x);
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = "grey";
ctx.moveTo(x,y);
ctx.lineTo(mouse.x, mouse.y);
ctx.stroke();
}
}
}else if(dragging){
dragging = false;
}
drawBalls();
}
/*-------------------------------------------------------------------------------------
answer code END
---------------------------------------------------------------------------------------*/
/** SimpleFullCanvasMouse.js begin **/
const CANVAS_ELEMENT_ID = "canv";
const U = undefined;
var w, h, cw, ch; // short cut vars
var canvas, ctx, mouse;
var globalTime = 0;
var createCanvas, resizeCanvas, setGlobals;
var L = typeof log === "function" ? log : function(d){ console.log(d); }
createCanvas = function () {
var c,cs;
cs = (c = document.createElement("canvas")).style;
c.id = CANVAS_ELEMENT_ID;
cs.position = "absolute";
cs.top = cs.left = "0px";
cs.zIndex = 1000;
document.body.appendChild(c);
return c;
}
resizeCanvas = function () {
if (canvas === U) { canvas = createCanvas(); }
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx = canvas.getContext("2d");
if (typeof setGlobals === "function") { setGlobals(); }
}
setGlobals = function(){ cw = (w = canvas.width) / 2; ch = (h = canvas.height) / 2; balls.length = 0; }
mouse = (function(){
function preventDefault(e) { e.preventDefault(); }
var mouse = {
x : 0, y : 0, w : 0, alt : false, shift : false, ctrl : false, buttonRaw : 0,
over : false, // mouse is over the element
bm : [1, 2, 4, 6, 5, 3], // masks for setting and clearing button raw bits;
mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
};
var m = mouse;
function mouseMove(e) {
var t = e.type;
m.x = e.offsetX; m.y = e.offsetY;
if (m.x === U) { m.x = e.clientX; m.y = e.clientY; }
m.alt = e.altKey; m.shift = e.shiftKey; m.ctrl = e.ctrlKey;
if (t === "mousedown") { m.buttonRaw |= m.bm[e.which-1]; }
else if (t === "mouseup") { m.buttonRaw &= m.bm[e.which + 2]; }
else if (t === "mouseout") { m.buttonRaw = 0; m.over = false; }
else if (t === "mouseover") { m.over = true; }
else if (t === "mousewheel") { m.w = e.wheelDelta; }
else if (t === "DOMMouseScroll") { m.w = -e.detail; }
if (m.callbacks) { m.callbacks.forEach(c => c(e)); }
e.preventDefault();
}
m.addCallback = function (callback) {
if (typeof callback === "function") {
if (m.callbacks === U) { m.callbacks = [callback]; }
else { m.callbacks.push(callback); }
} else { throw new TypeError("mouse.addCallback argument must be a function"); }
}
m.start = function (element, blockContextMenu) {
if (m.element !== U) { m.removeMouse(); }
m.element = element === U ? document : element;
m.blockContextMenu = blockContextMenu === U ? false : blockContextMenu;
m.mouseEvents.forEach( n => { m.element.addEventListener(n, mouseMove); } );
if (m.blockContextMenu === true) { m.element.addEventListener("contextmenu", preventDefault, false); }
}
m.remove = function () {
if (m.element !== U) {
m.mouseEvents.forEach(n => { m.element.removeEventListener(n, mouseMove); } );
if (m.contextMenuBlocked === true) { m.element.removeEventListener("contextmenu", preventDefault);}
m.element = m.callbacks = m.contextMenuBlocked = U;
}
}
return mouse;
})();
var done = function(){
window.removeEventListener("resize",resizeCanvas)
mouse.remove();
document.body.removeChild(canvas);
canvas = ctx = mouse = U;
L("All done!")
}
resizeCanvas(); // create and size canvas
mouse.start(canvas,true); // start mouse on canvas and block context menu
window.addEventListener("resize",resizeCanvas); // add resize event
function update(timer){ // Main update loop
globalTime = timer;
display(); // call demo code
// continue until mouse right down
if (!(mouse.buttonRaw & 2)) { requestAnimationFrame(update); } else { done(); }
}
requestAnimationFrame(update);
/** SimpleFullCanvasMouse.js end **/
(发布了问题作者的解决方案,将其移动到答案空间)。
问题解决了!我忘了考虑"indexMass-1"磁盘的位置来计算Math.atan2
函数的新角度。
- 为什么HTML5拖放的目标是孩子?(可排序列表)
- html5拖放文件-在提交整个表单时上传
- html5拖放确定拖动项目的来源
- HTML5拖放;Drop-使用jQuery处理事件
- HTML5拖放访问属性
- HTML5拖放-如何删除IE上的默认重影图像
- HTML5拖放新图像并替换Div中的现有图像
- 更改HTML5拖放中的重影图像
- CSS / HTML5:拖放后保持悬停状态
- HTML5 拖放 - 如何捕获和交换数据属性
- HTML5拖放效果Allowed和dropEffect
- HTML5拖放不会掉落
- html5拖放上传在chrome中中断
- 如果放置目标被完全占用,则不会触发HTML5-拖放-ondragover事件
- html5 拖放 - items.webkitGetAsEntry() 方法不存在
- HTML5拖放:使用JavaScript在文本框中加载文本文件
- HTML5 拖放与插入之前
- HTML5 拖放以重新排列网格中的小部件
- 防止HTML5拖放功能多次放置同一元素
- HTML5拖放没有AJAX是可能的