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

236
16 сентября 2018, 14:10

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

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

  • вызвать 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); 
}
<canvas id="can" width="200" height="200" />

Answer 1

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

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

Answer 2

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

READ ALSO
Ошибка в консоли при запуске gulp

Ошибка в консоли при запуске gulp

При запуске gulp watch появляется ошибка

204
Минимизация AJAX

Минимизация AJAX

Господа, на странице есть много разных форм с отправкой через AJAX

222
Как проскролить до нужного элемента в модальном окне?

Как проскролить до нужного элемента в модальном окне?

по нажатию на якорь открываю мод окно и делаю скролл до нужного элементаНо скролится не содержимое окна, а всё, что за ним

200
Как получить value всех input-ов?

Как получить value всех input-ов?

Мне нужно получить значение всех инпутов, каждый из которых находится в родительском блокеИ полученные значения, сложить в сумме

219