Vue3項目頁面實現(xiàn)echarts圖表漸變色的動態(tài)配置的實現(xiàn)步驟
一、背景
在開發(fā)可配置業(yè)務平臺時,需要實現(xiàn)讓用戶對項目內echarts圖表的動態(tài)配置,讓用戶脫離代碼也能實現(xiàn)簡單的圖表樣式配置。顏色作為圖表樣式的重要組成部分,其配置方式是項目要解決的重點問題。
二、難點整理
1.可以支持漸變的顏色選擇器。
2.漸變色選擇器取值結果與echarts圖表配置要求的轉換
3.圖表配置資源存儲與更新機制
4.漸變取色器位置配置導致取色器選中的色值發(fā)生變化觸發(fā)事件change頻繁被調用,引起最近選取顏色列表更新速率過快和圖表刷新過快造成的資源浪費問題等。
三、解決方法
1.漸變取色器
因為開發(fā)時間問題,這里直接選用了TDesign的顏色選擇器,具體可以參考TDesign官網
<div style="display: inline-block;vertical-align: middle;margin-right: 1rem;" >
<t-color-picker
v-model="lineColor"
defaultValue="#409cff"
:show-primary-color-preview="false"
:recent-colors="recentcolorsList"
@change="changeLineColorThrottle"
/>
</div>2.漸變色值類型轉換
tdesign取色器獲取到的漸變色值為形如:linear-gradient(90deg,rgba(0, 0, 0, 1) 0%,rgb(73, 106, 220) 100%) 的css漸變色寫法
echarts圖表配置漸變色寫法為:
"color":{
"colorStops":[
{"offset":0,"color":"#2378f7"},
{"offset":0.5,"color":"#2378f7"},
{"offset":1,"color":"#83bff6"}
],
"x":0,
"y":0,
"x2":1,
"y2":0.5,
"type":"linear",
"global":false
}因此需要對兩種類型進行轉換。
1) rgb與hex色值表達轉換
function hexToRgb(hex){
let str = hex.replace("#", "");
if (str.length % 3) {
return "hex格式不正確!";
}
//獲取截取的字符長度
let count = str.length / 3;
//根據(jù)字符串的長度判斷是否需要 進行冪次方
let power = 6 / str.length;
let r = parseInt("0x" + str.substring(0 * count, 1 * count)) ** power;
let g = parseInt("0x" + str.substring(1 * count, 2 * count)) ** power;
let b = parseInt("0x" + str.substring(2 * count)) ** power;
return `rgb(${r}, ${g}, $)`;
}
function rgbToHex(rgb){
let arr = rgb
.replace("rgb", "")
.replace("(", "")
.replace(")", "")
.split(",");
// 轉十六進制
let h = parseInt(arr[0]).toString(16);
let e = parseInt(arr[1]).toString(16);
let x = parseInt(arr[2]).toString(16);
if(h.length<2){
h = '0' + h
}
if(e.length<2){
e = '0' + e
}
if(x.length<2){
x = '0' + x
}
return "#" + h + e + x;
}2)角度與坐標參數(shù)x,x2,y,y2的轉化
css中角度是指水平線和漸變線之間的角度,逆時針方向計算。換句話說,0deg 將創(chuàng)建一個從下到上的漸變,90deg 將創(chuàng)建一個從左到右的漸變。【注意很多瀏覽器(Chrome、Safari、firefox等)的使用了舊的標準,即 0deg 將創(chuàng)建一個從左到右的漸變,90deg 將創(chuàng)建一個從下到上的漸變。換算公式 90 - x = y 其中 x 為標準角度,y為非標準角度。】
linear-gradient() 函數(shù)會繪制出一系列與漸變線垂直的彩色線,每條線都匹配與漸變線相交點的顏色。這條漸變線由包含漸變圖形的容器的中心點和一個角度來定義的。漸變線上的顏色值是由不同的點來定義,包括起始點、終點,以及兩者之間的可選的中間點(中間點可以有多個)。起點是漸變線上代表起始顏色值的點。終點是漸變線上代表最終顏色值的點。這兩個點都是由漸變線和從最近的頂點發(fā)出的垂直線之間的交叉點定義的。
echarts圖表實際上是基于canvas畫布展示的,因此需要將起點和終點轉換在canvas坐標系中。

css漸變角度與canvas坐標參數(shù)轉換過程
需要注意的是,漸變線與矩形的交點可能落在水平線上,也可能落在垂直線上,所以需要分情況進行計算,實現(xiàn)代碼如下:
function calculateGradientCoordinate(
width,
height,
angle = 180,
) {
if (angle >= 360) angle = angle - 360;
if (angle < 0) angle = angle + 360;
angle = Math.round(angle);
// 當漸變軸垂直于矩形水平邊上的兩種結果
if (angle === 0) {
return {
x0: Math.round(width / 2),
y0: height,
x1: Math.round(width / 2),
y1: 0,
};
}
if (angle === 180) {
return {
x0: Math.round(width / 2),
y0: 0,
x1: Math.round(width / 2),
y1: height,
};
}
// 當漸變軸垂直于矩形垂直邊上的兩種結果
if (angle === 90) {
return {
x0: 0,
y0: Math.round(height / 2),
x1: width,
y1: Math.round(height / 2),
};
}
if (angle === 270) {
return {
x0: width,
y0: Math.round(height / 2),
x1: 0,
y1: Math.round(height / 2),
};
}
// 從矩形左下角至右上角的對角線的角度
const alpha = Math.round(
(Math.asin(width / Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2))) *
180) /
Math.PI,
);
// 當漸變軸分別于矩形的兩條對角線重合情況下的四種結果
if (angle === alpha) {
return {
x0: 0,
y0: height,
x1: width,
y1: 0,
};
}
if (angle === 180 - alpha) {
return {
x0: 0,
y0: 0,
x1: width,
y1: height,
};
}
if (angle === 180 + alpha) {
return {
x0: width,
y0: 0,
x1: 0,
y1: height,
};
}
if (angle === 360 - alpha) {
return {
x0: width,
y0: height,
x1: 0,
y1: 0,
};
}
// 以矩形的中點為坐標原點,向上為Y軸正方向,向右為X軸正方向建立直角坐標系
let x0 = 0,
y0 = 0,
x1 = 0,
y1 = 0;
// 當漸變軸與矩形的交點落在水平線上
if (
angle < alpha || // 處于第一象限
(angle > 180 - alpha && angle < 180) || // 處于第二象限
(angle > 180 && angle < 180 + alpha) || // 處于第三象限
angle > 360 - alpha // 處于第四象限
) {
// 將角度乘以(PI/180)即可轉換為弧度
const radian = (angle * Math.PI) / 180;
// 當在第一或第四象限,y是height / 2,否則y是-height / 2
const y = angle < alpha || angle > 360 - alpha ? height / 2 : -height / 2;
const x = Math.tan(radian) * y;
// 當在第一或第二象限,l是width / 2 - x,否則l是-width / 2 - x
const l =
angle < alpha || (angle > 180 - alpha && angle < 180)
? width / 2 - x
: -width / 2 - x;
const n = Math.pow(Math.sin(radian), 2) * l;
x1 = x + n;
y1 = y + n / Math.tan(radian);
x0 = -x1;
y0 = -y1;
}
// 當漸變軸與矩形的交點落在垂直線上
if (
(angle > alpha && angle < 90) || // 處于第一象限
(angle > 90 && angle < 180 - alpha) || // 處于第二象限
(angle > 180 + alpha && angle < 270) || // 處于第三象限
(angle > 270 && angle < 360 - alpha) // 處于第四象限
) {
// 將角度乘以(PI/180)即可轉換為弧度
const radian = ((90 - angle) * Math.PI) / 180;
// 當在第一或第二象限,x是width / 2,否則x是-width / 2
const x =
(angle > alpha && angle < 90) || (angle > 90 && angle < 180 - alpha)
? width / 2
: -width / 2;
const y = Math.tan(radian) * x;
// 當在第一或第四象限,l是height / 2 - y,否則l是-height / 2 - y
const l =
(angle > alpha && angle < 90) || (angle > 270 && angle < 360 - alpha)
? height / 2 - y
: -height / 2 - y;
const n = Math.pow(Math.sin(radian), 2) * l;
x1 = x + n / Math.tan(radian);
y1 = y + n;
x0 = -x1;
y0 = -y1;
}
// 坐標系更改為canvas標準,Y軸向下為正方向
x0 = Math.round(x0 + width / 2);
y0 = Math.round(height / 2 - y0);
x1 = Math.round(x1 + width / 2);
y1 = Math.round(height / 2 - y1);
return { x0, y0, x1, y1 };
}echarts坐標參數(shù)取值范圍為0~1,在傳參時可以直接將width和height設置為1。
坐標參數(shù)轉換回角度時,需要注意角度所在象限區(qū)域進行分情況討論。
x=0.5 y=0, x2=0.5, y2=1 從上到下
x=1 y=0.5, x2=0.5, y2=0 從下到上
x=0 y=0.5, x2=1, y2=0.5 從左到右
x=1 y=0.5, x2=0, y2=0.5 從右到左
實現(xiàn)代碼如下:
function getDeg(x1,y1,x2,y2){
var x = Math.abs(x1 - x2);
var y = Math.abs(y1 - y2);
var tan = x / y;
var radina = Math.atan(tan); //用反三角函數(shù)求弧度
var deg = Math.floor(180 / (Math.PI / radina)) || 0; //將弧度轉換成角度
/**
* 根據(jù)目標點判斷象限(注意是笛卡爾坐標)
* 一: +,+
* 二: -,+
* 三: -,+
* 一: +,-
*/
// * 二、三象限要加 180°
if (x2 < 0 && y2 >= 0) {
deg = 180 + deg;
}
if (x2 < 0 && y2 < 0) {
deg = 180 + deg;
}
// 一、二象限 === 0 就是 180°
if (deg === 0) {
if ((x2 >= 0 && y2 > 0) || (x2 <= 0 && y2 > 0)) {
deg = 180 + deg;
}
}
if ((x2 <= 0 && y2 > 0)) {
deg = 180 + deg;
}
if ((x2 >= 0 && y2 > 0) ) {
deg = - (180 + deg);
}
return deg;
}此外還需要對色值RGB和HEX進行轉換,代碼如下:
function hexToRgb(hex){
let str = hex.replace("#", "");
if (str.length % 3) {
return "hex格式不正確!";
}
//獲取截取的字符長度
let count = str.length / 3;
//根據(jù)字符串的長度判斷是否需要 進行冪次方
let power = 6 / str.length;
let r = parseInt("0x" + str.substring(0 * count, 1 * count)) ** power;
let g = parseInt("0x" + str.substring(1 * count, 2 * count)) ** power;
let b = parseInt("0x" + str.substring(2 * count)) ** power;
return `rgb(${r}, ${g}, $)`;
}
function rgbToHex(rgb){
let arr = rgb
.replace("rgb", "")
.replace("(", "")
.replace(")", "")
.split(",");
// 轉十六進制
let h = parseInt(arr[0]).toString(16);
let e = parseInt(arr[1]).toString(16);
let x = parseInt(arr[2]).toString(16);
if(h.length<2){
h = '0' + h
}
if(e.length<2){
e = '0' + e
}
if(x.length<2){
x = '0' + x
}
return "#" + h + e + x;
}以上就是css線性漸變格式與echarts線性漸變格式最為關鍵的轉換過程。
2.圖表配置資源存儲與更新機制
圖表的整體配置資源項目通過store進行存儲,在頁面掛載時進行讀取解析。因為onMounted生命周期內,chart配置數(shù)據(jù)可能還未存儲至store中,在此階段進行初始化可能造成屬性值不存在等報錯。由于配置組件在配置時已經記錄配置值,如果對chartOption進行持續(xù)監(jiān)聽會造成沖突,導致取色器異常。因此需要設置flag值判斷頁面是否為初始化。初始化代碼如下:
watch(()=>chartOption,()=>{
//chartOption的反復修改和監(jiān)聽導致的沖突
if(pageUpdateFlag.value){
if('lineStyle' in chartOption.value['series'][0]){
if('type' in chartOption.value['series'][0]['lineStyle'] ){
lineType.value = chartOption.value['series'][0]['lineStyle']['type']
}
if('width' in chartOption.value['series'][0]['lineStyle'] ){
lineWidth.value = chartOption.value['series'][0]['lineStyle']['width']
}
if('color' in chartOption.value['series'][0]['lineStyle'] ){
let color = chartOption.value['series'][0]['lineStyle']['color']
if(typeof color !== 'string' && 'type' in color){
let x = color.x
let y = color.y
let x1 = color.x2
let y1 = color.y2
let deg = 90
deg = getDeg(x-0.5,y-0.5,x1-0.5,y1-0.5)
let colorStr = ''
//漸變顏色選擇器里的color要用rgb形式
for(let i =0 ; i < color.colorStops.length;i++){
let curColor = color.colorStops[i].color
if( color.colorStops[i].color[0] == '#'){
curColor = hexToRgb(color.colorStops[i].color[0])
}
colorStr = colorStr + ',' + color.colorStops[i].color + ' ' + color.colorStops[i].offset*100 + '%'
}
let str = "linear-gradient("+deg+'deg'+colorStr+')'
lineColor.value = str
}else{
lineColor.value = color
}
}
}
if('showBackground' in chartOption.value['series'][0]){
isArea.value = true
if('color' in chartOption.value['series'][0]['areaStyle']){
let color = chartOption.value['series'][0]['areaStyle']['color']
if(typeof color !== 'string' && 'type' in color){
let x = color.x
let y = color.y
let x1 = color.x2
let y1 = color.y2
let deg = 90
deg = getDeg(x-0.5,y-0.5,x1-0.5,y1-0.5)
let colorStr = ''
//漸變顏色選擇器里的color要用rgb形式
for(let i =0 ; i < color.colorStops.length;i++){
let curColor = color.colorStops[i].color
if( color.colorStops[i].color[0] == '#'){
curColor = hexToRgb(color.colorStops[i].color[0])
}
colorStr = colorStr + ',' + color.colorStops[i].color + ' ' + color.colorStops[i].offset*100 + '%'
}
let str = "linear-gradient("+deg+'deg'+colorStr+')'
areaColor.value = str
}else{
areaColor.value = color
}
}
}else {
isArea.value = false
}
pageUpdateFlag.value = false
}
},{deep:true})3.利用節(jié)流函數(shù)解決change事件頻繁調用導致的一系列沖突問題
在使用TDesign取色器時,通過滑動條調整漸變色偏移量會導致change事件頻繁調用,如果直接在change函數(shù)中獲取value添加到最近使用顏色列表就會造成最近使用列表顏色刷新過快,近似值添加過多的問題。因此需要使用節(jié)流函數(shù)來避免這一問題。同時change的頻繁調用也會導致圖表數(shù)據(jù)更新過快,造成資源浪費等問題。因此本項目使用了節(jié)流函數(shù)對change的整體事件進行了處理。
節(jié)流函數(shù):
import { ref } from 'vue';
export default function useThrottle(fn, delay) {
const canRun = ref(true);
return (...args) => {
if (!canRun.value) return;
canRun.value = false;
setTimeout(() => {
fn(...args);
canRun.value = true;
}, delay);
};
};
取色器change事件:
const changeLineColorThrottle = useThrottle(changeLineColor,400)
function changeLineColor(value,context){
addRecentColor(recentcolorsList,value)
//不能直接value及其值進行任何
//判斷取色為純色還是漸變,注意value為rgb(115, 171, 230)或linear-gradient(90deg,rgb(241, 29, 0) 0%,rgb(73, 106, 220) 100%)
console.log('判斷取色為純色還是漸變',value,lineColor.value,lineColor.value.includes('linear'));
let curColor = value
if(curColor.includes('linear')){
//漸變色,需要解析轉換
let colorObj = parseLinearColor(curColor)
chartOption.value['series'][0]['lineStyle']['color'] = deepcopy(colorObj)
}else{
chartOption.value['series'][0]['lineStyle']['color'] = curColor
}
//??!注意:頻繁設置狀態(tài)可能紊亂,導致漸變取色器為黑色
store.changeChartIsChange(true,"option")
}添加最近使用顏色函數(shù)和格式轉換函數(shù):
function addRecentColor(recentcolorsList,color){
//添加前應判斷數(shù)組內是否有重復的色值,有則刪除原色值
for(let i =0 ; i < recentcolorsList.value.length; i++){
if(recentcolorsList.value[i] == color){
recentcolorsList.value.splice(i,1)
}
}
recentcolorsList.value.unshift(color)
}
function parseLinearColor(curColor){
//滑動取色時會造成顏色添加頻繁
let colorObj = {
"colorStops": [],
"x":0,
"y":0,
"x1":0,
"y1":0,
"type":"linear",
"global":false
}
let deg = curColor.split('linear-gradient(')[1].split('deg')[0]
let colorList = curColor.split('deg')[1].split(',r')
for(let i = 1; i < colorList.length; i++ ){
let arr = colorList[i].split(") ")
let color = 'r'+arr[0]+')'
if(color.includes('rgba')){
color = color.replace('rgba','rgb')
}
color = rgbToHex(color)
let offset = arr[1].split("%")[0]/100
colorObj.colorStops.push({"offset":offset,"color":color})
}
let {x0,y0,x1,y1} = calculateGradientCoordinate(1,1,deg)
colorObj.x = x0
colorObj.x2 = x1
colorObj.y = y0
colorObj.y2 = y1
console.log(deg,x0,y0,x1,y1,"deg轉化x0,y0,x1,y1");
return colorObj
}以上就是實現(xiàn)echarts圖表漸變色的動態(tài)配置的關鍵步驟。
以上就是Vue3項目頁面實現(xiàn)echarts圖表漸變色的動態(tài)配置的實現(xiàn)步驟的詳細內容,更多關于Vue3 echarts圖表漸變配置的資料請關注腳本之家其它相關文章!
相關文章
elementUI的table表格改變數(shù)據(jù)不更新問題解決
最近在做vue的項目時發(fā)現(xiàn)了一個問題,今天就來解決一下,本文主要介紹了elementUI的table表格改變數(shù)據(jù)不更新問題解決,感興趣的可以了解一下2022-02-02
vue 中基于html5 drag drap的拖放效果案例分析
本文通過三個案例給大家介紹了vue 中基于html5 drag drap的拖放效果 ,需要的朋友可以參考下2018-11-11
vue+element-ui?校驗開始時間與結束時間的實現(xiàn)代碼
這篇文章主要介紹了vue+element-ui?校驗開始時間與結束時間的代碼實現(xiàn),最主要的需求是開始時間不能早于當前時間,感興趣的朋友跟隨小編一起看看吧2024-07-07

