Как соеденить точки на графике линиями?(html)
Всем привет. Сейчас делаю одно задание, в котором нужно сверстать несколько слайдеров, в которых ползунки будут соединяться кривой линией и, естественно, эта линия должна передвигать вместе с ползунками. У меня в макете есть svg элементы линий, но мне это, как-то, не особо помогает. Я, вроде как, понимаю, что нужно настраивать линию примерно как показано тут:
function makePath(x1, y1, x2, y2) {
const weight = 0.3;
const dx = (x2 - x1) * weight;
const c1 = x1 + dx;
const c2 = x2 - dx;
return `<path d="M${x1},${y1} C${c1},${y1} ${c2},${y2} ${x2},${y2}" stroke-width="2" stroke="#DDD" fill="transparent"/>`;
}
Но, хоть я и, примерно, понял смысл происходящего и даже смог привязать линии к ползункам, все равно не получается сделать их хотя бы немного похожими(не обязательно прям точь в точь), как на макете. У меня получаются только какие-то странные некрасивые кривые) И, в общем, неужели это все(изменения координат в зависимости от x/y) и правда нужно настраивать руками, может есть какой-то более простой способ?
Ответы (1 шт):
Примерно так для второй переносимой точки. Ее можно двигать. Рандомизацию отклонений контрольных точек не делал, можно добавить.
const svg = document.querySelector('svg');
const circle = svg.querySelector('#movable');
const path = svg.querySelector('path');
const wave_wid = 50; // Width of the WAVE
const path_start = [20,40]; // Start-point of path
let path_end = [200,140]; // Initial end-point of path
drawPath(path_end); // Drawing path with initial end-point
function drawPath(coords){ // Calculate, build path string, and assign it to path
path_end = coords;
const len = getLen(path_start, path_end);
const wave_num = Math.floor(len/wave_wid);
const points = [];
let isUp = true;
for(let i = 0; i < wave_num; i++){
points.push([i*wave_wid, 0]); // start-end points of curves
if(isUp){
points.push([i*wave_wid + wave_wid/2, -wave_wid/2]); // control points of curves upwards (may be randomized)
} else {
points.push([i*wave_wid + wave_wid/2, wave_wid/2]); // control points of curves downwards (may be randomized)
}
isUp = !isUp; // change direction of contron point tilt
}
points.push([len, 0]); // last point
path.setAttribute('d', createPath(points));
const angle = getAngle(path_start, path_end);
path.setAttribute('transform', `translate(20,40), rotate(${angle})`); // move to start-point and rotate the path
}
function getLen(first_point, second_point){ // Calculate path length (straightly drawn line)
return Math.hypot(Math.abs(first_point[1] - second_point[1]), Math.abs(first_point[0] - second_point[0]));
}
function getAngle(first_point, second_point){ // Calculate angle of path rotation
return Math.atan2(first_point[1] - second_point[1], first_point[0] - second_point[0]) * 180 / Math.PI + 180;
}
function createPath(points){ // Build path string using Quadratic Bezier
let result = `M 0 0 `;
for(let i = 1; i < points.length; i+=2){
result += `Q ${points[i][0]} ${points[i][1]} ${points[i+1][0]} ${points[i+1][1]} `;
}
return result;
}
// The circle manipulation part of code
let movable = false;
circle.addEventListener('mousedown', () => {movable = true});
document.body.addEventListener('mouseup', () => {movable = false});
svg.addEventListener('mousemove', (e) => {if(movable) moveCircle(e)});
function moveCircle(event){
const x = event.clientX;
circle.setAttribute('cx', x);
drawPath([x,140]);
}
<svg width="600px" height="180px">
<circle r="10" cx="20" cy="40"></circle>
<circle r="10" cx="200" cy="140" id="movable"></circle>
<path d="" stroke="black" fill="none"></path>
</svg>
Здесь после рефакторинга и добавил рандомизацию. Теперь функция drawPath() принимает два элемента SVG типа circle (имеющими атрибуты cx и сy). А также элемент path, атрибуту d которого будет назначена строка нового пути.
Так функция получается более универсальной, при изменении положения одного круга, можно вызывать ее два раза поочередно с соседними кругами, передавать существующие элементы пути, например, по id вроде c1-c2 при передаче кругов с id с1 и c2 соответственно.
При перемещении круга (второго) из-за рандомизации путь "играет", можно разкомментировать строку без добавления рандомных отклонений.
const svg = document.querySelector('svg');
const path = svg.querySelector('path');
const circle_movable = svg.querySelector('#movable');
const circle_static = svg.querySelector('#static');
const wave_wid = 50; // Width of the WAVE
drawPath(circle_static, circle_movable, path); // Drawing path with initial end-point
function drawPath(first_circle, second_circle, path){ // Calculate, build path string, and assign it to path
const path_start = [+first_circle.getAttribute('cx'), +first_circle.getAttribute('cy')];
const path_end = [+second_circle.getAttribute('cx'), +second_circle.getAttribute('cy')];
const len = getLen(path_start, path_end);
const wave_num = Math.floor(len/wave_wid);
const points = []; // key points of curved path
let isUp = true;
for(let i = 0; i < wave_num; i++){
points.push([i*wave_wid, 0]); // start-end points of curves
let sign = 1;
if(isUp){
sign = -1;
}
points.push([i*wave_wid + wave_wid/2, sign * wave_wid/2 + randomPlusMinus(10)]); // control points of curves with randomizing
// points.push([i*wave_wid + wave_wid/2, sign * wave_wid/2]); // Without randomizing
isUp = !isUp; // change direction of contron point tilt
}
points.push([len, 0]); // last point
path.setAttribute('d', createPath(points));
const angle = getAngle(path_start, path_end);
path.setAttribute('transform', `translate(20,40), rotate(${angle})`); // move to start-point and rotate the path
}
function getLen(first_point, second_point){ // Calculate path length (straightly drawn line)
return Math.hypot(first_point[1] - second_point[1], first_point[0] - second_point[0]);
}
function getAngle(first_point, second_point){ // Calculate angle of path rotation
return Math.atan2(second_point[1] - first_point[1], second_point[0] - first_point[0]) * 180 / Math.PI;
}
function randomPlusMinus(range){
return Math.floor(Math.random() * ( range * 2 + 1) ) - range;
}
function createPath(points){ // Build path string using Quadratic Bezier
let result = `M 0 0 `;
for(let i = 1; i < points.length; i+=2){
result += `Q ${points[i][0]} ${points[i][1]} ${points[i+1][0]} ${points[i+1][1]} `;
}
return result;
}
// The circle manipulation part of code
let movable = false;
circle_movable.addEventListener('mousedown', () => {movable = true});
document.addEventListener('mouseup', () => {movable = false});
document.addEventListener('mousemove', (e) => {if(movable) moveCircle(e)});
function moveCircle(event){
const x = event.clientX;
circle_movable.setAttribute('cx', x);
drawPath(circle_static, circle_movable, path);
}
<svg width="600px" height="180px">
<circle r="10" cx="20" cy="40" id="static"></circle>
<circle r="10" cx="200" cy="140" id="movable"></circle>
<path d="" stroke="black" fill="none"></path>
</svg>
