及时绘制HTML5/Javascript画布路径
假设我有一条路径:及时绘制HTML5/Javascript画布路径,javascript,canvas,html5-canvas,Javascript,Canvas,Html5 Canvas,假设我有一条路径: var context = canvas.getContext('2d'); context.beginPath(); context.moveTo(100, 20); context.lineTo(200, 160); context.quadraticCurveTo(230, 200, 250, 120); context.bezierCurveTo(290, -40, 300, 200, 400, 150); context.lineTo(500, 90); conte
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(100, 20);
context.lineTo(200, 160);
context.quadraticCurveTo(230, 200, 250, 120);
context.bezierCurveTo(290, -40, 300, 200, 400, 150);
context.lineTo(500, 90);
context.lineWidth = 5;
context.strokeStyle = 'blue';
context.stroke();
这将一次打印所有路径:
如何将路径拆分为给定长度的子路径?例如:context.splitCurrentPathIntoSubPath(0,0.75)
应该只返回路径的前3/4
我想用这个来实现一个动画。如果有更简单的方法,也欢迎使用。这是我的解决方案,基本上是在路径上方绘制一个矩形,然后每次帧更新都将矩形移动1 X位置,因此长方体将缓慢地移离路径,看起来像是在绘制动画路径 我已经为您保存了:)这是独立代码
window.addEventListener( "load", firstLoaded, false);
then = Date.now();
setInterval(main, 1); // Execute as fast as possible
var cube_x_position = 0;
function main()
{
context.beginPath();
context.moveTo(100, 20);
context.lineTo(200, 160);
context.quadraticCurveTo(230, 200, 250, 120);
context.bezierCurveTo(290, -40, 300, 200, 400, 150);
context.lineTo(500, 90);
context.lineWidth = 5;
context.strokeStyle = 'blue';
context.stroke();
context.fillRect(cube_x_position, 0, canvasHolder.width, canvasHolder.height);
if(cube_x_position < canvasHolder.width)
{
cube_x_position += 1;
}
}
function firstLoaded()
{
canvasHolder = document.getElementById( 'canvas' );
context = canvasHolder.getContext('2d');
context.fillStyle = "#AAAAAA";
context.fillRect( 0, 0, 500, 500);
}
window.addEventListener(“加载”,firstLoaded,false);
然后=Date.now();
设置间隔(主,1);//尽可能快地执行
var cube_x_位置=0;
函数main()
{
context.beginPath();
上下文。moveTo(100,20);
lineTo(200160);
二次曲线(230200250120);
贝塞尔曲线图(290,-40300200400150);
lineTo(500,90);
context.lineWidth=5;
context.strokeStyle='blue';
stroke();
context.fillRect(立方体x位置,0,canvashholder.width,canvashholder.height);
if(立方体x位置<画布支架宽度)
{
立方体x位置+=1;
}
}
函数firstLoaded()
{
canvasHolder=document.getElementById('canvas');
context=canvashholder.getContext('2d');
context.fillStyle=“#AAAAAA”;
fillRect(0,0500500);
}
在使用D3.js设置SVG圆弧动画时,我遇到了类似的问题。我的解决方案借鉴了这一点。它不是最直观的,但通常用于D3动画。它需要仔细设置虚线偏移和线长度
我已经用这种技术修改了上面的JSFIDLE。请注意,即使线本身循环,这也会起作用
关于行长的注释:
// T is an interval between 0.00 and 1.00
// To divide a Line into 300 parts you would call the function 300 times
// with T increasing 1.00/300 each time
function getLineXYatPercent(startPt,endPt,T) {
var dx = endPt.x-startPt.x;
var dy = endPt.y-startPt.y;
var X = startPt.x + dx*T;
var Y = startPt.y + dy*T;
return( {x:X,y:Y} );
}
// T is an interval between 0.00 and 1.00
// To divide a Quadratic Curve into 300 parts you would call the function 300 times
// with T increasing 1.00/300 each time
function getQuadraticBezierXYatT(startPt,controlPt,endPt,T) {
var x = Math.pow(1-T,2) * startPt.x + 2 * (1-T) * T * controlPt.x + Math.pow(T,2) * endPt.x;
var y = Math.pow(1-T,2) * startPt.y + 2 * (1-T) * T * controlPt.y + Math.pow(T,2) * endPt.y;
return( {x:x,y:y} );
}
// T is an interval between 0.00 and 1.00
// To divide a BezierCurve into 300 parts you would call the function 300 times
// with T increasing 1.00/300 each time
function getCubicBezierXYatT(startPt,controlPt1,controlPt2,endPt,T){
var x=CubicN(T,startPt.x,controlPt1.x,controlPt2.x,endPt.x);
var y=CubicN(T,startPt.y,controlPt1.y,controlPt2.y,endPt.y);
return({x:x,y:y});
}
// cubic helper formula at T distance
function CubicN(T, a,b,c,d) {
var t2 = T * T;
var t3 = t2 * T;
return a + (-a * 3 + T * (3 * a - a * T)) * T
+ (3 * b + T * (-6 * b + b * 3 * T)) * T
+ (c * 3 - c * 3 * T) * t2
+ d * t3;
}
var dx=point2.x-point1.x;
var dy=point2.y-point1.y;
var distance=Math.sqrt(dx*dx+dy*dy);
此实现要求您知道行的近似长度,以便您可以将length var设置为大于它。对于贝塞尔曲线和二次曲线,这很棘手,但仍然可以做到()。在我的演示中,我使用了反复试验的方法,发现你的大约是608px。将length设置为10000可能会确保线条始终正确绘制,但代价是每毫秒调用大量不必要的间隔回调。底线是:如果你关心性能,找出贝塞尔公式的东西;如果没有,请将该变量设置为高
代码:
// T is an interval between 0.00 and 1.00
// To divide a Line into 300 parts you would call the function 300 times
// with T increasing 1.00/300 each time
function getLineXYatPercent(startPt,endPt,T) {
var dx = endPt.x-startPt.x;
var dy = endPt.y-startPt.y;
var X = startPt.x + dx*T;
var Y = startPt.y + dy*T;
return( {x:X,y:Y} );
}
// T is an interval between 0.00 and 1.00
// To divide a Quadratic Curve into 300 parts you would call the function 300 times
// with T increasing 1.00/300 each time
function getQuadraticBezierXYatT(startPt,controlPt,endPt,T) {
var x = Math.pow(1-T,2) * startPt.x + 2 * (1-T) * T * controlPt.x + Math.pow(T,2) * endPt.x;
var y = Math.pow(1-T,2) * startPt.y + 2 * (1-T) * T * controlPt.y + Math.pow(T,2) * endPt.y;
return( {x:x,y:y} );
}
// T is an interval between 0.00 and 1.00
// To divide a BezierCurve into 300 parts you would call the function 300 times
// with T increasing 1.00/300 each time
function getCubicBezierXYatT(startPt,controlPt1,controlPt2,endPt,T){
var x=CubicN(T,startPt.x,controlPt1.x,controlPt2.x,endPt.x);
var y=CubicN(T,startPt.y,controlPt1.y,controlPt2.y,endPt.y);
return({x:x,y:y});
}
// cubic helper formula at T distance
function CubicN(T, a,b,c,d) {
var t2 = T * T;
var t3 = t2 * T;
return a + (-a * 3 + T * (3 * a - a * T)) * T
+ (3 * b + T * (-6 * b + b * 3 * T)) * T
+ (c * 3 - c * 3 * T) * t2
+ d * t3;
}
var dx=point2.x-point1.x;
var dy=point2.y-point1.y;
var distance=Math.sqrt(dx*dx+dy*dy);
HTML
无法启动webgl
JavaScript
canvasHolder = document.getElementById( 'canvas' );
context = canvasHolder.getContext('2d');
context.fillStyle = 'white';
var w = canvasHolder.width, h = canvasHolder.height;
context.fillRect( 0, 0, w, h);
//set the direction the line draws in
//1->ltr | -1->rtl
var dir = -1;
//IMPORTANT: this must be set to greater than the length
//of the line
var length = 608;
//the speed of the line draw
var speed = 1;
var progress = 0;
var lineInterval;
//Go!
context.globalCompositeOperation='copy';
drawLine();
function drawLine() {
//this clears itself once the line is drawn
lineInterval = setInterval(updateLine, 1);
}
function updateLine() {
//define the line
defineLine();
if(progress<length)
{
progress+=speed;
moveDash(progress, dir);
} else {
clearInterval(lineInterval);
}
}
function defineLine() {
context.beginPath();
context.moveTo(100, 20);
context.lineTo(200, 160);
context.quadraticCurveTo(230, 200, 250, 120);
context.bezierCurveTo(290, -40, 300, 200, 400, 150);
context.lineTo(500, 90);
context.lineWidth = 5;
context.strokeStyle = 'blue';
}
function moveDash(frac, dir) {
//default direction right->left
var dir = dir || -1
context.setLineDash([length]);
context.lineDashOffset = dir*(frac+length);
context.stroke();
}
canvasHolder=document.getElementById('canvas');
context=canvashholder.getContext('2d');
context.fillStyle='white';
var w=画布支架.宽度,h=画布支架.高度;
fillRect(0,0,w,h);
//设置线绘制的方向
//1->ltr |-1->rtl
var-dir=-1;
//重要提示:必须将其设置为大于长度
//排队
变量长度=608;
//划线的速度
无功转速=1;
var进程=0;
var线性区间;
//走!
context.globalCompositeOperation='copy';
抽绳();
函数drawLine(){
//一旦绘制了线,这将自动清除
lineInterval=setInterval(updateLine,1);
}
函数updateLine(){
//界定界线
定义线();
如果(左)
var dir=dir | |-1
context.setLineDash([length]);
context.lineDashOffset=dir*(分形+长度);
stroke();
}
使用等距点绘制复杂路径的演示:
// T is an interval between 0.00 and 1.00
// To divide a Line into 300 parts you would call the function 300 times
// with T increasing 1.00/300 each time
function getLineXYatPercent(startPt,endPt,T) {
var dx = endPt.x-startPt.x;
var dy = endPt.y-startPt.y;
var X = startPt.x + dx*T;
var Y = startPt.y + dy*T;
return( {x:X,y:Y} );
}
// T is an interval between 0.00 and 1.00
// To divide a Quadratic Curve into 300 parts you would call the function 300 times
// with T increasing 1.00/300 each time
function getQuadraticBezierXYatT(startPt,controlPt,endPt,T) {
var x = Math.pow(1-T,2) * startPt.x + 2 * (1-T) * T * controlPt.x + Math.pow(T,2) * endPt.x;
var y = Math.pow(1-T,2) * startPt.y + 2 * (1-T) * T * controlPt.y + Math.pow(T,2) * endPt.y;
return( {x:x,y:y} );
}
// T is an interval between 0.00 and 1.00
// To divide a BezierCurve into 300 parts you would call the function 300 times
// with T increasing 1.00/300 each time
function getCubicBezierXYatT(startPt,controlPt1,controlPt2,endPt,T){
var x=CubicN(T,startPt.x,controlPt1.x,controlPt2.x,endPt.x);
var y=CubicN(T,startPt.y,controlPt1.y,controlPt2.y,endPt.y);
return({x:x,y:y});
}
// cubic helper formula at T distance
function CubicN(T, a,b,c,d) {
var t2 = T * T;
var t3 = t2 * T;
return a + (-a * 3 + T * (3 * a - a * T)) * T
+ (3 * b + T * (-6 * b + b * 3 * T)) * T
+ (c * 3 - c * 3 * T) * t2
+ d * t3;
}
var dx=point2.x-point1.x;
var dy=point2.y-point1.y;
var distance=Math.sqrt(dx*dx+dy*dy);
匀速行驶综述
“速度”定义为每单位时间的距离
“匀速”因此是指每单位时间以一致的指定距离行驶
因此,以每1/60秒2像素的速度沿路径移动就是匀速移动的一个例子
要移动2个像素,必须沿路径计算距离上一个点2个像素的点
以匀速增量绘制包含直线和曲线的路径需要数百次小计算
以下是如何确定沿路径均匀分布的点阵列:
- 将路径划分为线段:直线、二次曲线、贝塞尔曲线、直线
- 使用定义每个线段的数学公式(参见下面的公式)沿每个线段计算多个(300+)点,并将这些点放入数组中
- 按顺序沿每个点行走并计算点之间的距离(请参见下面的公式)
- 保留沿点行驶的累计距离的总和
- 当当前移动的点达到指定长度时,将该点保存在第二个数组中
// T is an interval between 0.00 and 1.00
// To divide a Line into 300 parts you would call the function 300 times
// with T increasing 1.00/300 each time
function getLineXYatPercent(startPt,endPt,T) {
var dx = endPt.x-startPt.x;
var dy = endPt.y-startPt.y;
var X = startPt.x + dx*T;
var Y = startPt.y + dy*T;
return( {x:X,y:Y} );
}
// T is an interval between 0.00 and 1.00
// To divide a Quadratic Curve into 300 parts you would call the function 300 times
// with T increasing 1.00/300 each time
function getQuadraticBezierXYatT(startPt,controlPt,endPt,T) {
var x = Math.pow(1-T,2) * startPt.x + 2 * (1-T) * T * controlPt.x + Math.pow(T,2) * endPt.x;
var y = Math.pow(1-T,2) * startPt.y + 2 * (1-T) * T * controlPt.y + Math.pow(T,2) * endPt.y;
return( {x:x,y:y} );
}
// T is an interval between 0.00 and 1.00
// To divide a BezierCurve into 300 parts you would call the function 300 times
// with T increasing 1.00/300 each time
function getCubicBezierXYatT(startPt,controlPt1,controlPt2,endPt,T){
var x=CubicN(T,startPt.x,controlPt1.x,controlPt2.x,endPt.x);
var y=CubicN(T,startPt.y,controlPt1.y,controlPt2.y,endPt.y);
return({x:x,y:y});
}
// cubic helper formula at T distance
function CubicN(T, a,b,c,d) {
var t2 = T * T;
var t3 = t2 * T;
return a + (-a * 3 + T * (3 * a - a * T)) * T
+ (3 * b + T * (-6 * b + b * 3 * T)) * T
+ (c * 3 - c * 3 * T) * t2
+ d * t3;
}
var dx=point2.x-point1.x;
var dy=point2.y-point1.y;
var distance=Math.sqrt(dx*dx+dy*dy);
沿二次曲线计算点:
// T is an interval between 0.00 and 1.00
// To divide a Line into 300 parts you would call the function 300 times
// with T increasing 1.00/300 each time
function getLineXYatPercent(startPt,endPt,T) {
var dx = endPt.x-startPt.x;
var dy = endPt.y-startPt.y;
var X = startPt.x + dx*T;
var Y = startPt.y + dy*T;
return( {x:X,y:Y} );
}
// T is an interval between 0.00 and 1.00
// To divide a Quadratic Curve into 300 parts you would call the function 300 times
// with T increasing 1.00/300 each time
function getQuadraticBezierXYatT(startPt,controlPt,endPt,T) {
var x = Math.pow(1-T,2) * startPt.x + 2 * (1-T) * T * controlPt.x + Math.pow(T,2) * endPt.x;
var y = Math.pow(1-T,2) * startPt.y + 2 * (1-T) * T * controlPt.y + Math.pow(T,2) * endPt.y;
return( {x:x,y:y} );
}
// T is an interval between 0.00 and 1.00
// To divide a BezierCurve into 300 parts you would call the function 300 times
// with T increasing 1.00/300 each time
function getCubicBezierXYatT(startPt,controlPt1,controlPt2,endPt,T){
var x=CubicN(T,startPt.x,controlPt1.x,controlPt2.x,endPt.x);
var y=CubicN(T,startPt.y,controlPt1.y,controlPt2.y,endPt.y);
return({x:x,y:y});
}
// cubic helper formula at T distance
function CubicN(T, a,b,c,d) {
var t2 = T * T;
var t3 = t2 * T;
return a + (-a * 3 + T * (3 * a - a * T)) * T
+ (3 * b + T * (-6 * b + b * 3 * T)) * T
+ (c * 3 - c * 3 * T) * t2
+ d * t3;
}
var dx=point2.x-point1.x;
var dy=point2.y-point1.y;
var distance=Math.sqrt(dx*dx+dy*dy);
沿贝塞尔曲线计算点:
// T is an interval between 0.00 and 1.00
// To divide a Line into 300 parts you would call the function 300 times
// with T increasing 1.00/300 each time
function getLineXYatPercent(startPt,endPt,T) {
var dx = endPt.x-startPt.x;
var dy = endPt.y-startPt.y;
var X = startPt.x + dx*T;
var Y = startPt.y + dy*T;
return( {x:X,y:Y} );
}
// T is an interval between 0.00 and 1.00
// To divide a Quadratic Curve into 300 parts you would call the function 300 times
// with T increasing 1.00/300 each time
function getQuadraticBezierXYatT(startPt,controlPt,endPt,T) {
var x = Math.pow(1-T,2) * startPt.x + 2 * (1-T) * T * controlPt.x + Math.pow(T,2) * endPt.x;
var y = Math.pow(1-T,2) * startPt.y + 2 * (1-T) * T * controlPt.y + Math.pow(T,2) * endPt.y;
return( {x:x,y:y} );
}
// T is an interval between 0.00 and 1.00
// To divide a BezierCurve into 300 parts you would call the function 300 times
// with T increasing 1.00/300 each time
function getCubicBezierXYatT(startPt,controlPt1,controlPt2,endPt,T){
var x=CubicN(T,startPt.x,controlPt1.x,controlPt2.x,endPt.x);
var y=CubicN(T,startPt.y,controlPt1.y,controlPt2.y,endPt.y);
return({x:x,y:y});
}
// cubic helper formula at T distance
function CubicN(T, a,b,c,d) {
var t2 = T * T;
var t3 = t2 * T;
return a + (-a * 3 + T * (3 * a - a * T)) * T
+ (3 * b + T * (-6 * b + b * 3 * T)) * T
+ (c * 3 - c * 3 * T) * t2
+ d * t3;
}
var dx=point2.x-point1.x;
var dy=point2.y-point1.y;
var distance=Math.sqrt(dx*dx+dy*dy);
两点之间的距离:
// T is an interval between 0.00 and 1.00
// To divide a Line into 300 parts you would call the function 300 times
// with T increasing 1.00/300 each time
function getLineXYatPercent(startPt,endPt,T) {
var dx = endPt.x-startPt.x;
var dy = endPt.y-startPt.y;
var X = startPt.x + dx*T;
var Y = startPt.y + dy*T;
return( {x:X,y:Y} );
}
// T is an interval between 0.00 and 1.00
// To divide a Quadratic Curve into 300 parts you would call the function 300 times
// with T increasing 1.00/300 each time
function getQuadraticBezierXYatT(startPt,controlPt,endPt,T) {
var x = Math.pow(1-T,2) * startPt.x + 2 * (1-T) * T * controlPt.x + Math.pow(T,2) * endPt.x;
var y = Math.pow(1-T,2) * startPt.y + 2 * (1-T) * T * controlPt.y + Math.pow(T,2) * endPt.y;
return( {x:x,y:y} );
}
// T is an interval between 0.00 and 1.00
// To divide a BezierCurve into 300 parts you would call the function 300 times
// with T increasing 1.00/300 each time
function getCubicBezierXYatT(startPt,controlPt1,controlPt2,endPt,T){
var x=CubicN(T,startPt.x,controlPt1.x,controlPt2.x,endPt.x);
var y=CubicN(T,startPt.y,controlPt1.y,controlPt2.y,endPt.y);
return({x:x,y:y});
}
// cubic helper formula at T distance
function CubicN(T, a,b,c,d) {
var t2 = T * T;
var t3 = t2 * T;
return a + (-a * 3 + T * (3 * a - a * T)) * T
+ (3 * b + T * (-6 * b + b * 3 * T)) * T
+ (c * 3 - c * 3 * T) * t2
+ d * t3;
}
var dx=point2.x-point1.x;
var dy=point2.y-point1.y;
var distance=Math.sqrt(dx*dx+dy*dy);
祝你的项目好运!遗憾的是,这只适用于穿过一个轴的稳定路径。这不符合我的目的,因为我的路径可能有一个循环,路径应该以统一的速度绘制。无论如何,谢谢!我在寻找解决方案时偶然发现了你的代码!-)我非常喜欢这个想法,并且自己做数学,但可悲的是,这需要重新迭代。当设置1px的增量时,线的轮廓变得非常硬,看起来不再自然。你的解决方案可能适用于更多的问题,但我认为