[CSS, JavaScript, Работа с векторной графикой, VueJS] Как реализовать динамическую диаграмму для Vue на основе SVG
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Бывает, что на сайте, в корпоративной IT-системе или другом ПО нужно отображать круговые диаграммы с какими-либо данными. Например, это может быть таймер для отсчета времени или индикатор, сколько товаров продано в той или иной категории. Если это статическое изображение, конечно, можно обойтись форматом svg, png или gif. Однако, зачастую нужно показать данные в динамике – например, для мониторинга или просто для привлечения внимания пользователей, для создания красивой анимации при загрузке сайта. Делимся примером, как можно построить диаграмму из элементов SVG с помощью JS и CSS.
Представим, что нам нужно сделать диаграмму без подключения сторонних библиотек. Создадим независимый VUE-компонент, начнём с основы – шаблона.
<template>
<svg class="diagram" viewBox="0 0 42 42">
<!—- Фоновый круг, подложка -->
<circle
:class="classCircleBack"
:r="radius"
cx="50%"
cy="50%"
:stroke-dashoffset="dashoffset"
/>
<!—- Внутренний круг, это и есть сам график -->
<circle
ref="mainDiagram"
class="front"
:class="classCircleFront"
:stroke-dasharray="dasharray"
:r="radius"
cx="50%"
cy="50%"
/>
<!—- Спутник, необязательный элемент, но может пригодиться —->
<!—- в зависимости от вашей задачи -->
<circle
v-if="satellite"
class="satellite"
r="1"
cx="101%"
cy="50%"
:style="rotate"
/>
</svg>
</template>
С помощью атрибутов CX и CY указываем смещение центра окружности фигуры <CIRCLE />, тем самым размещая объект по центру холста. При этом важно помнить, что в svg холстах независимый отсчёт координат, и единицей измерения не являются пиксели. Не забываем в теге svg прописать атрибут viewBox=«0 0 42 42» для указания размера холста.
Далее рассмотрим код на VUE, частично с добавлением TypeScript. Вся “магия” построения диаграммы и работа с анимацией здесь будут происходить за счет изменений свойства stroke-dasharray в теге circle – но сначала опишем входящие свойства компонента:
import Vue, { PropType } from 'vue'
export default Vue.extend({
name: 'Diagram',
props: {
// Свойство, которое принимает массив чисел, где:
// нулевой элемент – это длина отрезка для видимой части stroke-dasharray
// элемент с индексом 1 – это длина всего отрезка
// например, из 78 яблок продано 25, значит, пропс должен принять [25, 78]
dataDasharray: {
type: Array as PropType<number[]>,
required: true
},
// Радиус, необязательный пропс, можно указывать в любых единицах измерения
radius: {
type: String,
required: false,
},
// Кастомный css класс для стилизации фоновой фигуры круга
classCircleBack: {
type: String,
},
// Кастомный css класс для стилизации внешнего круга
classCircleFront: {
type: String,
},
// Если нужна фигура-спутник
satellite: {
type: Boolean,
}
},
data() {
return {
dasharray: '0 0', // начальные данные псевдомассива отрезка
dashoffset: '100', // Длина окружности для фигуры подложки – определяет смещение обводки относительно начального положения
radiusBaseVal: 0, // про эту переменную чуть ниже
circumference: 0 // Длина окружности, которую вычислим позже
}
}
Ранее мы подготовили компонент, теперь к нему нужно описать функционал.
// Вычисляемые свойства для анимации спутника
computed: {
rotate(): string {
// Поворот спутника относительно центра холста для инлайнового стиля
return `transform: rotate(${this.degRotate}deg);`
},
degRotate() {
// Вычисляем градус поворота спутника, основываясь на пропсе dataDasharray
const percent: number = Number(
((this.dataDasharray[0] * 100) / this.dataDasharray[1]).toFixed(1)
)
return (-360 * (percent / 100) - 90).toFixed(1)
}
}
В этом фрагменте указана числовая константа -360. Она необходима для того, чтобы зеркально отобразить вращение «спутника», иначе сателлит будет двигаться против часовой стрелки – вопреки основной анимации круговой диаграммы.
Подчеркнем, что к следующему шагу мы переходим именно тогда, когда компонент vue будет смонтирован – чтобы обеспечить доступность ref. Затем выставим значения двух важных переменных:
mounted() {
this.radiusBaseVal = (this.$refs.mainDiagram as any).r.baseVal.value
this.circumference = 2 * Math.PI * this.radiusBaseVal
}
radiusBaseVal – переменная, которая получает внутренний программный радиус фигуры <circle/>. Важно отметить, что этот радиус не связан с радиусом в разметке html.
circumference – переменная для хранения длины окружности (привет школьной тригонометрии!).
В данном компоненте присутствует всего лишь один математический метод для установки значений атрибутов stroke-dashoffset в фигуре подложки и атрибута stroke-dasharray во внешней фигуре. Впоследствии мы применим к ним анимацию.
methods: {
setLengthDasharray(percent, circumference) {
const offset = circumference - (percent / 100) * circumference
this.dasharray = `${offset} ${circumference}`
this.dashoffset = circumference.toFixed(3)
}
}
Далее вся соль заключается в вотчере, где и стартует “магия” компонента:
watch: {
dataDasharray: {
handler() {
// вычисляем процентное соотношение данных из пропса dataDasharray
const percent = (
(this.dataDasharray[0] * 100) / this.dataDasharray[1]
).toFixed(1)
// Сетим длину оффсетов для нашей диаграммы
this.setLengthDasharray(percent, this.circumference)
},
deep: true, // Глубокое отслеживание пропса dataDasharray
immediate: true // запуск handler функции при mounted компонента
}
}
И завершающий этап: немного базовых стилей:
<style lang="scss" scoped>
.diagram {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
overflow: visible;
}
circle {
fill: transparent;
stroke: rgb(255, 255, 255);
stroke-width: 0.6px;
transform-origin: center;
transform: rotate(-90deg); /* Обязательно повернём circle элемент, так как отсчёт dasharray будет начинаться справа, а не сверху. */
transition: stroke-dasharray 1s ease;
&.front {
stroke: rgb(255, 255, 255);
}
}
.satellite {
fill: #fff;
will-change: transform; // скажем браузеру, что ожидается трансформирование для отправки на GPU
stroke-width: 0.4px;
transition: transform 1s ease;
}
</style>
Выводы
Итак, если вам нужно показать в приложении различные данные в виде диаграммы, есть разные пути решения. Для сложных вычислений можно обратиться к сторонним библиотекам (например, D3), но этот способ зачастую привносит в проект дополнительные риски: например, ухудшение runtime сайта и показателей поисковой оптимизации, увеличение time to Interactive и script execution, а как следствие – недовольство пользователей. Если большие вычисления не требуются, то бывает достаточно простых нативных инструментов – именно этот способ мы рассмотрели в статье.
Посмотреть полный пример и поэкспериментировать с исходным кодом можно здесь.
Спасибо за внимание! Надеемся, что этот пример был вам полезен.
===========
Источник:
habr.com
===========
Похожие новости:
- [JavaScript, Программирование] Из каталога NPM удалили четыре зловредных пакета
- [JavaScript, Программирование, Разработка веб-сайтов] Настройка Webpack 5 с нуля (перевод)
- [JavaScript, Программирование, Java, TypeScript] TypeScript для бэкенд-разработки (перевод)
- [JavaScript, Компьютерная анимация, Звук] Демка MONOSPACE, победитель Assembly ONLINE 2020, умещается в 1021 байт
- [Apache, Big Data, Hadoop] Spark schemaEvolution на практике
- [JavaScript, Программирование, Разработка веб-сайтов] Использование «глобального» await в JavaScript (перевод)
- [CSS, HTML, JavaScript] Элементарный автоматический слайдер на основе библиотеки RevolveR :: 10 строчек кода
- [Разработка веб-сайтов, JavaScript, API, ReactJS] Соединяем Redux и GraphQL на простом примере
- [JavaScript, NoSQL, Node.JS] Инструменты Node.js разработчика. Какие ODM нам нужны
- [CSS, JavaScript, Интерфейсы, HTML] Динамическое меню c поддержкой touch move и mouse move на RevolveR
Теги для поиска: #_css, #_javascript, #_rabota_s_vektornoj_grafikoj (Работа с векторной графикой), #_vuejs, #_vue, #_js, #_css, #_diagramma (диаграмма), #_dinamicheskij_grafik (динамический график), #_blog_kompanii_simbirsoft (
Блог компании SimbirSoft
), #_css, #_javascript, #_rabota_s_vektornoj_grafikoj (
Работа с векторной графикой
), #_vuejs
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 20:36
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Бывает, что на сайте, в корпоративной IT-системе или другом ПО нужно отображать круговые диаграммы с какими-либо данными. Например, это может быть таймер для отсчета времени или индикатор, сколько товаров продано в той или иной категории. Если это статическое изображение, конечно, можно обойтись форматом svg, png или gif. Однако, зачастую нужно показать данные в динамике – например, для мониторинга или просто для привлечения внимания пользователей, для создания красивой анимации при загрузке сайта. Делимся примером, как можно построить диаграмму из элементов SVG с помощью JS и CSS. Представим, что нам нужно сделать диаграмму без подключения сторонних библиотек. Создадим независимый VUE-компонент, начнём с основы – шаблона. <template>
<svg class="diagram" viewBox="0 0 42 42"> <!—- Фоновый круг, подложка --> <circle :class="classCircleBack" :r="radius" cx="50%" cy="50%" :stroke-dashoffset="dashoffset" /> <!—- Внутренний круг, это и есть сам график --> <circle ref="mainDiagram" class="front" :class="classCircleFront" :stroke-dasharray="dasharray" :r="radius" cx="50%" cy="50%" /> <!—- Спутник, необязательный элемент, но может пригодиться —-> <!—- в зависимости от вашей задачи --> <circle v-if="satellite" class="satellite" r="1" cx="101%" cy="50%" :style="rotate" /> </svg> </template> С помощью атрибутов CX и CY указываем смещение центра окружности фигуры <CIRCLE />, тем самым размещая объект по центру холста. При этом важно помнить, что в svg холстах независимый отсчёт координат, и единицей измерения не являются пиксели. Не забываем в теге svg прописать атрибут viewBox=«0 0 42 42» для указания размера холста. Далее рассмотрим код на VUE, частично с добавлением TypeScript. Вся “магия” построения диаграммы и работа с анимацией здесь будут происходить за счет изменений свойства stroke-dasharray в теге circle – но сначала опишем входящие свойства компонента: import Vue, { PropType } from 'vue'
export default Vue.extend({ name: 'Diagram', props: { // Свойство, которое принимает массив чисел, где: // нулевой элемент – это длина отрезка для видимой части stroke-dasharray // элемент с индексом 1 – это длина всего отрезка // например, из 78 яблок продано 25, значит, пропс должен принять [25, 78] dataDasharray: { type: Array as PropType<number[]>, required: true }, // Радиус, необязательный пропс, можно указывать в любых единицах измерения radius: { type: String, required: false, }, // Кастомный css класс для стилизации фоновой фигуры круга classCircleBack: { type: String, }, // Кастомный css класс для стилизации внешнего круга classCircleFront: { type: String, }, // Если нужна фигура-спутник satellite: { type: Boolean, } }, data() { return { dasharray: '0 0', // начальные данные псевдомассива отрезка dashoffset: '100', // Длина окружности для фигуры подложки – определяет смещение обводки относительно начального положения radiusBaseVal: 0, // про эту переменную чуть ниже circumference: 0 // Длина окружности, которую вычислим позже } } Ранее мы подготовили компонент, теперь к нему нужно описать функционал. // Вычисляемые свойства для анимации спутника
computed: { rotate(): string { // Поворот спутника относительно центра холста для инлайнового стиля return `transform: rotate(${this.degRotate}deg);` }, degRotate() { // Вычисляем градус поворота спутника, основываясь на пропсе dataDasharray const percent: number = Number( ((this.dataDasharray[0] * 100) / this.dataDasharray[1]).toFixed(1) ) return (-360 * (percent / 100) - 90).toFixed(1) } } В этом фрагменте указана числовая константа -360. Она необходима для того, чтобы зеркально отобразить вращение «спутника», иначе сателлит будет двигаться против часовой стрелки – вопреки основной анимации круговой диаграммы. Подчеркнем, что к следующему шагу мы переходим именно тогда, когда компонент vue будет смонтирован – чтобы обеспечить доступность ref. Затем выставим значения двух важных переменных: mounted() {
this.radiusBaseVal = (this.$refs.mainDiagram as any).r.baseVal.value this.circumference = 2 * Math.PI * this.radiusBaseVal } radiusBaseVal – переменная, которая получает внутренний программный радиус фигуры <circle/>. Важно отметить, что этот радиус не связан с радиусом в разметке html. circumference – переменная для хранения длины окружности (привет школьной тригонометрии!). В данном компоненте присутствует всего лишь один математический метод для установки значений атрибутов stroke-dashoffset в фигуре подложки и атрибута stroke-dasharray во внешней фигуре. Впоследствии мы применим к ним анимацию. methods: {
setLengthDasharray(percent, circumference) { const offset = circumference - (percent / 100) * circumference this.dasharray = `${offset} ${circumference}` this.dashoffset = circumference.toFixed(3) } } Далее вся соль заключается в вотчере, где и стартует “магия” компонента: watch: {
dataDasharray: { handler() { // вычисляем процентное соотношение данных из пропса dataDasharray const percent = ( (this.dataDasharray[0] * 100) / this.dataDasharray[1] ).toFixed(1) // Сетим длину оффсетов для нашей диаграммы this.setLengthDasharray(percent, this.circumference) }, deep: true, // Глубокое отслеживание пропса dataDasharray immediate: true // запуск handler функции при mounted компонента } } И завершающий этап: немного базовых стилей: <style lang="scss" scoped>
.diagram { width: 100%; height: 100%; position: absolute; top: 0; left: 0; overflow: visible; } circle { fill: transparent; stroke: rgb(255, 255, 255); stroke-width: 0.6px; transform-origin: center; transform: rotate(-90deg); /* Обязательно повернём circle элемент, так как отсчёт dasharray будет начинаться справа, а не сверху. */ transition: stroke-dasharray 1s ease; &.front { stroke: rgb(255, 255, 255); } } .satellite { fill: #fff; will-change: transform; // скажем браузеру, что ожидается трансформирование для отправки на GPU stroke-width: 0.4px; transition: transform 1s ease; } </style> Выводы Итак, если вам нужно показать в приложении различные данные в виде диаграммы, есть разные пути решения. Для сложных вычислений можно обратиться к сторонним библиотекам (например, D3), но этот способ зачастую привносит в проект дополнительные риски: например, ухудшение runtime сайта и показателей поисковой оптимизации, увеличение time to Interactive и script execution, а как следствие – недовольство пользователей. Если большие вычисления не требуются, то бывает достаточно простых нативных инструментов – именно этот способ мы рассмотрели в статье. Посмотреть полный пример и поэкспериментировать с исходным кодом можно здесь. Спасибо за внимание! Надеемся, что этот пример был вам полезен. =========== Источник: habr.com =========== Похожие новости:
Блог компании SimbirSoft ), #_css, #_javascript, #_rabota_s_vektornoj_grafikoj ( Работа с векторной графикой ), #_vuejs |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 20:36
Часовой пояс: UTC + 5