console
class Point {
constructor(x, y) {
this.x = x
this.y = y
}
X = () => { return this.x }
Y = () => { return this.y }
}
let calcMoveLine = (line, offset) => {
let [p1, p2] = line
let p1p2 = [p1.X()-p2.X(), p1.Y()-p2.Y()]
let len = Math.hypot(p1p2[0], p1p2[1])
let n = null
if (p1.Y() < p2.Y()) {
n = new Point((p2.Y() - p1.Y())/len, (p1.X() - p2.X())/len)
} else {
n = new Point((p1.Y() - p2.Y())/len, (p2.X() - p1.X())/len)
}
let newline = [
new Point(p1.X() + n.X() * offset, p1.Y() + n.Y() * offset),
new Point(p2.X() + n.X() * offset, p2.Y() + n.Y() * offset)
]
return [
new Point(p1.X() + n.X() * offset, p1.Y() + n.Y() * offset),
new Point(p2.X() + n.X() * offset, p2.Y() + n.Y() * offset)
]
}
let calcMoveLine2 = (line, offset) => {
let [p1, p2] = line
let lVer = p2.Y() - p1.Y()
let lHor = p2.X() - p1.X()
let len = Math.hypot(lVer, lHor)
let rad = offset*1.0/len
let ver = lVer*rad*-1
let hor = lHor*rad*-1
return [
new Point(p1.X()+ver, p1.Y()+hor),
new Point(p2.X()+ver, p2.Y()+hor),
]
}
let _index = 1
let img = new Image()
img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAIBJREFUeNpiYBj04P////xAnADEzDAxJiI1egOpC0DMzsjI+JdY27SBeP9/CHgFxJzEaBIA4qb/qKCEGI0pQPwcTeN7grYCFcz4jx1MJ9afaUD8Ek3zc1KiRQiIJ6MZkEZq3BoA8Qmo5qfkJpAYkGZQYJJrACj6EpFT2MABgAADADTCu3dUzSmkAAAAAElFTkSuQmCC'
let drawLine = (ctx, lines) => {
ctx.beginPath()
ctx.lineCap = 'round'
lines.forEach((line) => {
ctx.moveTo(line[0].X(), line[0].Y())
ctx.lineTo(line[1].X(), line[1].Y())
ctx.lineWidth = 10
ctx.strokeStyle = '#0A404A'
})
ctx.stroke()
ctx.closePath()
ctx.beginPath()
ctx.lineCap = 'round'
lines.forEach((line) => {
let [d1, d2] = calcMoveLine(line, 2)
ctx.moveTo(line[0].X(), line[0].Y())
ctx.lineTo(line[1].X(), line[1].Y())
ctx.lineWidth = 6
ctx.strokeStyle = '#146573'
})
ctx.stroke()
ctx.closePath()
ctx.beginPath()
ctx.lineCap = 'round'
lines.forEach((line) => {
let [d3, d4] = calcMoveLine(line, 0)
ctx.lineWidth = 1
ctx.strokeStyle = '#1F9EBD'
ctx.moveTo(line[0].X(), line[0].Y())
ctx.lineTo(line[1].X(), line[1].Y())
ctx.stroke()
})
lines.forEach(([p1, p2]) => {
var offset = (_index%11) * 0.1;
console.log(offset)
generatePoints(p1, p2, 20, ctx , offset, img)
_index = ++_index>=10 ? 0 : _index
})
ctx.closePath()
}
let canvas = document.getElementById('c1')
let c2 = document.getElementById('c2')
canvas.width = 450
canvas.height = 700
c2.width= 450
c2.height = 700
let ctx = canvas.getContext('2d')
let ctx2 = c2.getContext('2d')
ctx2.fillStyle = '#ccc'
ctx2.fillRect(0, 0, 450, 700)
draw = () => {
ctx.clearRect(0, 0, 700, 700)
ctx.moveTo(-10, 0)
ctx.lineTo(10, 0)
ctx.moveTo(0, -10)
ctx.lineTo(0, 10)
ctx.stroke()
ctx.translate(200,200)
drawLine(ctx, [
[new Point(250, 200.0), new Point(200.0, 50.0)],
[new Point(100, 50.0), new Point(200.0, 50.0)],
[new Point(150, 50.0), new Point(100.0, 150.0)],
[new Point(250, 150.0), new Point(100.0, 50.0)],
])
ctx2.clearRect(0, 0, 350, 700)
ctx2.drawImage(canvas, 0, 0)
}
draw()
function generatePoints (startP, endP, stepSize = 30, ctx, aniOffset = 0.5, img) {
let radA = Math.atan((endP.Y() - startP.Y()) / (endP.X() - startP.X()))
if ((endP.X() - startP.X()) < 0) {
radA += Math.PI
}
let p1p2 = [startP.X() - endP.X(), startP.Y() - endP.Y()]
let dist = Math.hypot(p1p2[0], p1p2[1])
let points = []
const steps = dist / stepSize
const drawImg = (pX, pY) => {
if (img && ctx) {
ctx.save()
ctx.translate(pX, pY)
ctx.rotate(radA)
ctx.scale(0.5, 0.5)
ctx.drawImage(img, -img.width / 2, -img.height / 2)
ctx.restore()
}
}
for (let s = aniOffset; s <= steps; s += 1) {
const pX = Math.round(startP.X() + s * stepSize * Math.cos(radA))
const pY = Math.round(startP.Y() + s * stepSize * Math.sin(radA))
points.push([pX, pY])
drawImg(pX, pY)
}
return points
}
function arrowTo(ctx, p1, p2, arrowOptions) {
var opts = {
startOffset: 5,
endOffset: 5,
offset: 0,
color: '#E6E6FA',
activeIndex: -1,
activeColor: "#00FF00",
stepLength: 10,
justifyAlign: true,
arrowLength: 0,
arrowTheta: 25,
arrowHeadlen: 6,
arrowLineWidth: 1,
lineWidth: 1,
};
if (arrowOptions !== undefined && arrowOptions !== null) {
opts = Object.assign(opts, arrowOptions);
}
if (opts.lineWidth > 0) {
ctx.beginPath();
ctx.moveTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.strokeStyle = opts.color;
ctx.lineWidth = opts.lineWidth;
ctx.stroke();
ctx.closePath();
}
var len = Math.floor(Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)));
var loops = Math.floor((len - (opts.startOffset + opts.offset + opts.endOffset) + opts.stepLength) / (opts.arrowLength + opts.stepLength));
if (opts.justifyAlign === true) {
opts.stepLength = (len - (opts.startOffset + opts.offset + opts.endOffset) - (opts.arrowLength * loops)) / (loops - 1);
}
var highlightIndex = 0;
if (opts.activeIndex > 0) {
if ((opts.activeIndex % loops) === 0) {
highlightIndex = loops;
}
else {
highlightIndex = opts.activeIndex % loops;
}
}
var hudu = Math.atan2(p1.y - p2.y, p2.x - p1.x);
var p0 = { x: p1.x, y: p1.y };
var r;
var color;
for (var i = 0; i < loops; i++) {
r = (opts.startOffset + opts.offset) + (opts.arrowLength + opts.stepLength) * i;
p1 = {
x: p0.x + Math.cos(hudu) * r,
y: p0.y - Math.sin(hudu) * r
};
r = r + opts.arrowLength;
p2 = {
x: p0.x + Math.cos(hudu) * r,
y: p0.y - Math.sin(hudu) * r
};
if (highlightIndex > 0 && i === (highlightIndex - 1)) {
color = opts.activeColor;
}
else {
color = opts.color;
}
drawArrow(ctx, p1, p2, opts.arrowTheta, opts.arrowHeadlen, opts.arrowLineWidth, color);
}
}
function drawArrow(ctx, p1, p2, theta, headlen, width, color) {
theta = (theta !== undefined && theta !== null) ? theta : 25;
headlen = (headlen !== undefined && headlen !== null) ? headlen : 6;
width = (width !== undefined && width !== null) ? width : 1;
color = (color !== undefined && color !== null) ? color : '#000';
var angle = Math.atan2(p1.y - p2.y, p1.x - p2.x) * 180 / Math.PI,
angle1 = (angle + theta) * Math.PI / 180,
angle2 = (angle - theta) * Math.PI / 180,
topX = headlen * Math.cos(angle1),
topY = headlen * Math.sin(angle1),
botX = headlen * Math.cos(angle2),
botY = headlen * Math.sin(angle2);
ctx.save();
ctx.beginPath();
var arrowX = p2.x + topX;
var arrowY = p2.y + topY;
ctx.moveTo(arrowX, arrowY);
ctx.lineTo(p2.x, p2.y);
let arrowX2 = p2.x + botX;
let arrowY2 = p2.y + botY;
ctx.lineTo(arrowX2, arrowY2);
ctx.lineTo(arrowX, arrowY);
ctx.strokeStyle = color;
ctx.lineWidth = width;
ctx.stroke();
ctx.restore();
}
if (!Object.assign) {
Object.defineProperty(Object, "assign", {
enumerable: false,
configurable: true,
writable: true,
value: function (target, firstSource) {
"use strict";
if (target === undefined || target === null)
throw new TypeError("Cannot convert first argument to object");
var to = Object(target);
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
if (nextSource === undefined || nextSource === null) continue;
var keysArray = Object.keys(Object(nextSource));
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex];
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
if (desc !== undefined && desc.enumerable) to[nextKey] = nextSource[nextKey];
}
}
return to;
}
})
}
<canvas id='c1'></canvas>
<canvas id='c2'></canvas>