gpt4 book ai didi

javascript - 交互式弹线Canvas框架?

转载 作者:行者123 更新时间:2023-11-28 18:39:41 26 4
gpt4 key购买 nike

我想为我的网页创建一条交互式弹性线,当用户将鼠标悬停在该线上时,它会产生类似弹性效果或橡皮筋拉伸(stretch)效果的动画效果,并且鼠标离开该对象将恢复到原始形状。

演示对象

enter image description here

我是 HTML5 Canvas 的新手,我希望使用 JavaScript canvas 库来完成它,但是当我搜索最佳 Canvas 库时,我得到了更多选项,所以我很困惑选择哪一个来实现我的目标。 ThreeJs , fabricJs , PaperJs等是流行的 Canvas 库。

我想了解哪个框架最适合我的目标。

感谢并欣赏您的帮助心态。

最佳答案

您将需要使用反向运动学,或更具体地说 Kinematic Chain .

有许多或多或少复杂的方法。这是一个简单的方法,它允许您拖动端点,其余部分将随之而来。它并不完美,但可能可以达到此目的。

(反向)运动链

主要功能如下。它假设定义了一个包含点的数组以及一个距离变量:

// calculate IK chain (from last to first)
function calc() {

var angle, i, p1, p2;

for(i = points.length - 1; i > 0; i--) {
p1 = points[i]; // current point
p2 = points[i-1]; // previous point
angle = Math.atan2(p2.y - p1.y, p2.x - p1.x); // calc angle

p2.x = p1.x + distance * Math.cos(angle); // update previous point
p2.y = p1.y + distance * Math.sin(angle); // based on a fixed distance
}
}

请注意,distance 变量设置为固定长度,这是此处的关键。

我们现在需要做的就是检测鼠标拖动链中的最后一个点,其余的将随之而来。

链在行动

var c = document.querySelector("canvas"),
ctx = c.getContext("2d"),

// the chain - dragged by the *last* point
points = [
{x: 50, y: 50},
{x: 100, y: 60},
{x: 90, y: 90},
{x: 120, y: 110},
{x: 200, y: 80},
{x: 250, y: 130}
],
distance = 50,
isDown = false;

// set canvas size
resize();
window.onresize = resize;

function resize() {
c.width = window.innerWidth;
c.height = window.innerHeight;
calc();
render()
}

// handle mouse
c.onmousedown = function(e) {
var pos = getXY(e),
p = points[points.length - 1];

isDown = (pos.x > p.x - 7 && pos.x < p.x + 7 && pos.y > p.y - 7 && pos.y < p.y + 7);
};

window.onmousemove = function(e) {
if (!isDown) return;

points[points.length - 1] = getXY(e); // override last point with mouse position

// update chain and canvas
calc();
render();
};

window.onmouseup = function() {isDown = false};

// adjusted mouse position
function getXY(e) {
var rect = c.getBoundingClientRect();
return {
x: e.clientX - rect.left,
y: e.clientY - rect.top
}
}

// IK chain calculations
function calc() {

var angle, i, p1, p2;

for(i = points.length - 1; i > 0; i--) {
p1 = points[i];
p2 = points[i-1];
angle = Math.atan2(p2.y - p1.y, p2.x - p1.x);

p2.x = p1.x + distance * Math.cos(angle);
p2.y = p1.y + distance * Math.sin(angle);
}
}

// render line and handle
function render() {

var lp, radius = 7;

ctx.clearRect(0, 0, c.width, c.height);

// render current chain
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
for(var i = 1; i < points.length; i++) ctx.lineTo(points[i].x, points[i].y);
ctx.lineWidth = 3;
ctx.strokeStyle = "#07f";
ctx.stroke();

lp = points[points.length - 1];

// draw handle
ctx.beginPath();
ctx.moveTo(lp.x + radius, lp.y);
ctx.arc(lp.x, lp.y, radius, 0, Math.PI*2);
ctx.lineWidth = 2;
ctx.strokeStyle = "#900";
ctx.stroke();
}
<canvas></canvas>

回归根源

为了使其反弹,您将需要原始坐标,然后用链中的相应点进行插值。

这当然会发生在鼠标松开事件上。如果您愿意,可以使用缓动函数;在这种情况下,ease-out 可能是最合适的。

反弹

这个示例并不打算解决整个问题,也没有进行优化,但是,您应该能够了解所需内容的要点。根据需要进行修改。

为此,请执行以下操作:

  • 渲染函数现在接受一个参数,这样我们就可以向它提供任何点数组
  • 我们需要在固定点(原始路径)和 IK 链之间进行插值。为此,我们使用临时数组。
  • 我们在 t 为 [0, 1] 时制作动画
  • 完成后,我们将 IK 点重置为原始点并重新计算/渲染它。
  • 我还为链段添加了最小/最大,以展示如何使链本身更有弹性。

var c = document.querySelector("canvas"),
ctx = c.getContext("2d"),

// the fixed point chain
pointsFixed = [
{x: 50, y: 50},
{x: 100, y: 60},
{x: 90, y: 90},
{x: 120, y: 110},
{x: 200, y: 80},
{x: 250, y: 130}
],

// for the IK chain - dragged by the *last* point
points = [
{x: 50, y: 50},
{x: 100, y: 60},
{x: 90, y: 90},
{x: 120, y: 110},
{x: 200, y: 80},
{x: 250, y: 130}
],

min = 40, max = 70,
isDown = false,

// for animation
isPlaying = false,
t, step = 0.1; // t = [0, 1]

// set canvas size
resize();
window.onresize = resize;

function resize() {
c.width = window.innerWidth;
c.height = window.innerHeight;
calc();
render(points)
}

// handle mouse
c.onmousedown = function(e) {
var pos = getXY(e),
p = points[points.length - 1];

isDown = (pos.x > p.x - 7 && pos.x < p.x + 7 && pos.y > p.y - 7 && pos.y < p.y + 7);
};

window.onmousemove = function(e) {
if (!isDown) return;

points[points.length - 1] = getXY(e); // override last point with mouse position

// update chain and canvas
calc();
render(points);
};

window.onmouseup = function() {
if (isDown) {
isDown = false;
t = 0; // reset t for new animation
isPlaying = true; // allow looping
animate(); // start the animation
}
};

// adjusted mouse position
function getXY(e) {
var rect = c.getBoundingClientRect();
return {
x: e.clientX - rect.left,
y: e.clientY - rect.top
}
}

// IK chain calculations
function calc() {

var angle, i, p1, p2, dx, dy, distance;

for(i = points.length - 1; i > 0; i--) {
p1 = points[i];
p2 = points[i-1];
dx = p2.x - p1.x;
dy = p2.y - p1.y;
angle = Math.atan2(dy, dx);
distance = Math.max(min, Math.min(max, Math.sqrt(dx*dx + dy*dy)));

p2.x = p1.x + distance * Math.cos(angle);
p2.y = p1.y + distance * Math.sin(angle);
}
}

// interpolate and animate
function animate() {
if (isPlaying) {

// create a temp. array with interpolated points
for(var i = 0, p, pts = []; i < points.length; i++) {
pts.push(lerp(points[i], pointsFixed[i], t*t)); // t*t for easing
}

// increase t in animation
t += step;

// keep animating?
if (t <= 1) {
render(pts);
requestAnimationFrame(animate)
}
else {
// we're done
isPlaying = false;
points = pts;
calc();
render(points);
}
}

function lerp(p1, p2, t) {
return {
x: p1.x + (p2.x - p1.x) * t,
y: p1.y + (p2.y - p1.y) * t
}
}
}

// render line and handle
function render(points) {

var lp, radius = 7;

ctx.clearRect(0, 0, c.width, c.height);

// render current chain
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
for(var i = 1; i < points.length; i++) ctx.lineTo(points[i].x, points[i].y);
ctx.lineWidth = 3;
ctx.strokeStyle = "#07f";
ctx.stroke();

lp = points[points.length - 1];

// draw handle
ctx.beginPath();
ctx.moveTo(lp.x + radius, lp.y);
ctx.arc(lp.x, lp.y, radius, 0, Math.PI*2);
ctx.lineWidth = 2;
ctx.strokeStyle = "#900";
ctx.stroke();
}
<canvas></canvas>

关于javascript - 交互式弹线Canvas框架?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36335142/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com