Есть статичная круговая диаграмма.
Задача при наведении на сектор,
но последний пункт, если это слишком трудно, и костылем решить смогу (если получится справиться с первыми двумя пунктами), наложив одну диаграмму на другую.
Html структуру менять можно, главное получить результат. Ну и если уж без библиотек никак, то буду благодарен и за такое решение!)
var canvas = document.getElementById("can");
var ctx = canvas.getContext("2d");
var lastend = 0;
var data = [200, 60, 15]; //мои сектора
var myTotal = 0;
var myColor = ['red', 'green', 'blue']; //цвета
for (var e = 0; e < data.length; e++) {
myTotal += data[e];
}
for (var i = 0; i < data.length; i++) {
ctx.fillStyle = myColor[i];
ctx.beginPath();
ctx.moveTo(canvas.width / 2, canvas.height / 2);
// параметры: x, y, радиус, начальный угол, конечный угол, antiClockwise (boolean)
ctx.arc(canvas.width / 2, canvas.height / 2, canvas.height / 2, lastend, lastend + (Math.PI * 2 * (data[i] / myTotal)), false);
ctx.lineTo(canvas.width / 2, canvas.height / 2);
ctx.fill();
lastend += Math.PI * 2 * (data[i] / myTotal);
}
<canvas id="can" width="200" height="200" />
Рекомендую использовать для таких целей Pixi или любую другую библиотеку для работы с canvas. Ибо canvas api не имеет поддержки событий наведения или впринципе хранить какие-либо структурированные данные об отрисованных объектах. Для добавления подобных функций нужно писать обертки, которые будут хранить контекст и кастомные характеристики, а судя по твоему коду - тебе не до этого. А вот в Pixi все уже есть, fabric.js тоже отлично подойдет для этой цели.
PS на будущее имей в виду, что canvas рендерит только один кадр, если ты видишь взаимодействие с ним и какие-либо анимации (например анимация увеличения размера участка твоего графика), то они реализованы через нативные события и таймеры. на каждую 1/60 (60 кадров в сек.) canvas рендерится заново с новыми входными параметрами для абстрактных объектов.
class Pipe {
constructor(_parent) {
this.canvas = Pipe.genCanvasElement();
this.ctx = this.canvas.getContext("2d");
this.curent = null;
this.mousemove = Pipe.debounce(this, function (_e) {
// console.dir(_e)
let xM, yM;
xM = _e.offsetX;
yM = _e.offsetY;
let x, y;
x = this.canvas.width / 2;
y = this.canvas.height / 2;
let radians = Math.atan2(yM - y, xM - x);
let degrees = radians * 180 / Math.PI;
if ((360 + degrees) < 360) {
degrees = 360 + degrees;
radians = degrees * Math.PI / 180;
}
// console.log(`x:${xM} y:${yM} radians:${radians} degrees:${degrees}`)
// --
this.curent = radians;
// console.log('mousemove')
this.render();
}, 500);
this.mouseleave = Pipe.debounce(this, function (_e) {
this.curent = null;
// console.log('mouseleave')
if (Pipe.t)
clearTimeout(Pipe.t);
this.render();
}, 500);
this.canvas.addEventListener('mousemove', this.mousemove);
this.canvas.addEventListener('mouseleave', this.mouseleave);
_parent.appendChild(this.canvas);
this.render();
}
static debounce(self, f, ms) {
return function (...args) {
const onComplete = () => {
f.apply(self, args);
Pipe.t = null;
};
if (Pipe.t)
clearTimeout(Pipe.t);
Pipe.t = setTimeout(onComplete, ms);
};
}
static genCanvasElement() {
let c = document.createElement('canvas');
c.width = 200;
c.height = 200;
c.style.border = 'solid grey 1px';
return c;
}
static foo(ctx, fillStyle, x, y, r, startAngle, endAngle) {
ctx.beginPath();
ctx.fillStyle = fillStyle;
ctx.moveTo(x, y);
ctx.arc(x, y, r, startAngle, endAngle, false);
ctx.lineTo(x, y);
ctx.fill();
ctx.closePath();
}
static foo2(startAngle, curent, endAngle, fillStyle, x, y, r, j, ctx) {
// console.log(`
// startAngle:${startAngle}
// endAngle:${endAngle}
// this.curent:${this.curent}
// `)
if ((startAngle < curent) && (curent < endAngle)) {
fillStyle = 'aqua';
Pipe.foo(ctx, fillStyle, x, y, r + j, startAngle, endAngle);
}
else {
Pipe.foo(ctx, fillStyle, x, y, r, startAngle, endAngle);
}
}
render() {
let ctx = this.ctx;
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
let lastend = 0;
let data = [200, 60, 15]; //мои сектора
let myColor = ['red', 'green', 'blue']; //цвета
let j = this.canvas.width / 8;
let x, y, r;
x = this.canvas.width / 2;
y = this.canvas.height / 2;
r = y - j;
for (var i = 0; i < data.length; i++) {
let startAngle = lastend;
let endAngle = lastend + (Math.PI * 2 * (data[i] / 360));
let fillStyle = myColor[i];
Pipe.foo2(startAngle, this.curent, endAngle, fillStyle, x, y, r, j, ctx);
// --
lastend += Math.PI * 2 * (data[i] / 360);
}
if (lastend < Math.PI * 2) {
let startAngle = lastend;
let endAngle = Math.PI * 2;
let fillStyle = 'black';
Pipe.foo2(startAngle, this.curent, endAngle, fillStyle, x, y, r, j, ctx);
}
}
}
Pipe.t = null;
// ---
let pipe = new Pipe(document.body);
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
по нажатию на якорь открываю мод окно и делаю скролл до нужного элементаНо скролится не содержимое окна, а всё, что за ним
Мне нужно получить значение всех инпутов, каждый из которых находится в родительском блокеИ полученные значения, сложить в сумме