Страницы

Поиск по вопросам

пятница, 28 февраля 2020 г.

Как изменить цвет, размер сектора круга при наведении на него?

#javascript #html5 #canvas


Есть статичная круговая диаграмма. 

Задача при наведении на сектор, 


вызвать callback функцию
поменять размер сектора
цвет
а еще если можно выполнить дополнительную обводку по периметру окружности  пример
на картинке


но последний пункт, если это слишком трудно, и костылем решить смогу (если получится
справиться с первыми двумя пунктами), наложив одну диаграмму на другую.

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);
}




    


Ответы

Ответ 1



Предлагаю такой способ, считаем дистанцию от мышки до центра круга. Если дистанция меньше радиуса - мы навели на какой-то сектор, а на какой конкретно определяем по углу направления в сторону курсора мышки относительно центра круга. let data = [200, 60, 15]; let colors = ['red', 'green', 'blue']; let radius = canvas.height / 2 - 10; let ctx = canvas.getContext("2d"); let total = data.reduce((a, b) => a + b); let radiuses = [radius, radius, radius]; let activeAngle, activeIndex; function callback(active) { stat.textContent = colors[active] || 'none'; canvas.style.cursor = active > -1 ? 'pointer' : 'default'; } function render() { requestAnimationFrame(render); let currentActiveIndex; ctx.clearRect(0, 0, canvas.width, canvas.height); var lastend = 0; for (var i = 0; i < data.length; i++) { ctx.fillStyle = colors[i]; ctx.beginPath(); ctx.moveTo(canvas.width / 2, canvas.height / 2); let endAngle = lastend + (Math.PI * 2 * (data[i] / total)); let isActive = activeAngle && activeAngle > lastend && activeAngle < endAngle; currentActiveIndex = isActive ? i : currentActiveIndex; radiuses[i] += isActive ? 1 : -1; radiuses[i] = Math.min(Math.max(radius, radiuses[i]), radius+10); ctx.arc(canvas.width / 2, canvas.height / 2, radiuses[i], lastend, endAngle); ctx.lineTo(canvas.width / 2, canvas.height / 2); ctx.fill(); lastend += Math.PI * 2 * (data[i] / total); } activeIndex != currentActiveIndex && callback(activeIndex = currentActiveIndex); } requestAnimationFrame(render) canvas.addEventListener('mousemove', e => { let dy = e.layerY - canvas.height / 2; let dx = e.layerX - canvas.width / 2; let d = Math.sqrt(dx*dx + dy*dy) < radius; let a = Math.atan2(dy, dx); activeAngle = d ? a < 0 ? a + 2*Math.PI : a : null; }) none

Ответ 2



Рекомендую использовать для таких целей Pixi или любую другую библиотеку для работы с canvas. Ибо canvas api не имеет поддержки событий наведения или впринципе хранить какие-либо структурированные данные об отрисованных объектах. Для добавления подобных функций нужно писать обертки, которые будут хранить контекст и кастомные характеристики, а судя по твоему коду - тебе не до этого. А вот в Pixi все уже есть, fabric.js тоже отлично подойдет для этой цели. PS на будущее имей в виду, что canvas рендерит только один кадр, если ты видишь взаимодействие с ним и какие-либо анимации (например анимация увеличения размера участка твоего графика), то они реализованы через нативные события и таймеры. на каждую 1/60 (60 кадров в сек.) canvas рендерится заново с новыми входными параметрами для абстрактных объектов.

Ответ 3



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);

Комментариев нет:

Отправить комментарий