echarts渐变色与css渐变色互转(两个坐标点转角度)

1,437 阅读3分钟

前言

用于 echarts 的小伙伴都知道,他使用的渐变色写法和 css 的写法不一样。css 中直接使用角度定义渐变的方向,而 echarts 使用的是两个坐标点来进行标识方向(线性渐变)。

本文主要针对线性渐变的转换

那怎么在 css 中使用 echarts 的渐变色呢🤔? 也就是需要相互转换,下文将对此进行实现。

最终实现目标如下图演示: 左边是 echarts 柱状图,右边是 div 坐标转换.gif

两种渐变色格式

css 线性渐变使用方法 linear-gradient

<div
     style={{
        backgroundImage: `linear-gradient(90deg, red, blue)`
    }}
></div>

echarts 线性渐变使用方法


// echarts 颜色
const color = {
    type: "linear",  
    x: 0,
    y: 0.5,
    x2: 1,
    y2: 0.5, 
    colorStops: [
        {
                offset: 0,
                color: "red", // 0% 处的颜色
        },
        {
                offset: 1,
                color: "blue", // 100% 处的颜色
        },
    ]
};
        
const options = { 
     // ...
    series: [
        {
            // ...
            itemStyle: {
                 color,
            },
        },
    ],
};

代码目标

从上面可以看出来,其实就是 echarts 用了坐标的形式,而 css 使用的角度。所以针对坐标和角度进行转换即可

实现代码

具体实现见下面代码即可,懒得写了~

import React, { useEffect, useState, useRef } from "react";
// import Test from './views/Test';

/**
 * 线性渐变,前四个参数分别是 x0, y0, x2, y2
 * x y 语义
 *
 * 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 从右到左
 * 
 * css 中 0deg 是从上到下,顺时针方向是从左到右渐变(90deg => 左到右)
 *
 * */ 

function App() {
	const pointArr = useRef();
	const myChart = useRef();
	const [deg, setDeg] = useState();

	// echarts 颜色
	const color = {
		type: "linear", 
		// 左到右  90 deg
		x: 0,
		y: 0.5,
		x2: 1,
		y2: 0.5,

		colorStops: [
			{
				offset: 0,
				color: "red", // 0% 处的颜色
			},
			{
				offset: 1,
				color: "blue", // 100% 处的颜色
			},
		],
		global: false, // 缺省为 false
	};

	useEffect(() => {
		const format = ({ x: x1, y: y1, x2, y2 }) => {
			var getYAngle = function (cx, cy, x2, y2) {
				var x = Math.abs(cx - x2);
				var y = Math.abs(cy - y2);
				var tan = x / y;
				var radina = Math.atan(tan); //用反三角函数求弧度
				var angle = Math.floor(180 / (Math.PI / radina)) || 0; //将弧度转换成角度
				console.log("angle", angle);
				/**
				 * 根据目标点判断象限(注意是笛卡尔坐标)
				 * 一: +,+
				 * 二: -,+
				 * 三: -,+
				 * 一: +,-
				 */

				//  * 二、三象限要加 180°
				if (x2 < 0 && y2 >= 0) {
					angle = 180 + angle;
				}
				if (x2 < 0 && y2 < 0) {
					angle = 180 + angle;
				}

				// 一、二象限 === 0 就是 180°
				if (angle === 0) {
					if ((x2 >= 0 && y2 > 0) || (x2 <= 0 && y2 > 0)) {
						angle = 180 + angle;
					}
				}

				return angle;
			};

			/**
			 * 1、将 二维 坐标看成一个正方形([0, 0],[1, 0],[1, 1],[0, 1]), 坐落于一象限
			 * 2、根据二维坐标转一个新的坐标(相对于正方形中心点的,所以线段会贯穿正方形),
			 *    把相对于笛卡尔坐标系中心点的坐标,转为相对于正方形中心点的坐标
			 *    eg: x 0.5 => 0,
			 *        y 0   => -0.5
			 *
			 *        其实就是  x - 0.5,  y - 0.5
			 */
			const deg = getYAngle(color.x - 0.5, color.y - 0.5, color.x2 - 0.5, color.y2 - 0.5); 
		};
		const deg = format(color);

		// 计算成 css 渐变
		setDeg(deg);

		const options = {
			grid: {
				left: -100,
				top: 0,
				right: 0,
				bottom: 0,
				width: "180%",
			},
			xAxis: { show: false, data: ["数据"], boundaryGap: false },
			yAxis: { show: false },
			series: [
				{
					data: [100],
					type: "bar",
					itemStyle: {
						color,
					},
				},
			],
		};
		myChart.current = echarts.init(document.getElementById("main"));
		myChart.current.setOption(options);
	}, []);

	// 是否按下
	const isDwon = useRef();
	const onMouseDown = () => {
		isDwon.current = true; 
	};
	const onMouseUp = () => {
		isDwon.current = false; 
	};
	const onMouseMove = (event) => {
		if (!isDwon.current) return;
		const rece = pointArr.current.getBoundingClientRect();
		// 中心点
		const center = {
			x: rece.left + (rece.right - rece.left) / 2,
			y: rece.top + (rece.bottom - rece.top) / 2,
		};
		const rad = Math.atan2(center.y - event.pageY, center.x - event.pageX);
		let deg = rad / (Math.PI / 180) - 90; 
		if (deg < 0) {
			deg += 360;
		}
		setDeg(deg); 

		// 角度转坐标 
		const format2 = (deg) => { 

			// 假定旋转半径 0.5 
			const start = { x: 0, y: -0.5 };
			const end = {};
			end.x2 = start.x * Math.cos((deg * Math.PI) / 180) - start.y * Math.sin((deg * Math.PI) / 180);
			end.y2 = start.x * Math.sin((deg * Math.PI) / 180) + start.y * Math.cos((deg * Math.PI) / 180);
			 
      // 算出对应其他象限中对应的点
      end.x = 0 - end.x2;
      end.y = 0 - end.y2;

      // console.log(deg, end);
      return end; 
		};
		const { x, y, x2, y2 } = format2(deg);

		myChart.current.setOption({
			series: [
				{
					data: [100],
					type: "bar",
					itemStyle: {
						color: {
							...color,
							x: x + 0.5,
							y: y + 0.5,
							x2: x2 + 0.5,
							y2: y2 + 0.5,
						},
					},
				},
			],
		}); 
	};

	return (
		<>
			<div id="main" style={{ width: 300, height: 300, display: "inline-block", border: "3px solid green" }}></div>
			<div
				style={{
					backgroundImage: `linear-gradient(${deg}deg, red, blue)`,
					width: 300,
					height: 300,
					display: "inline-block",
					border: "3px solid green",
					marginLeft: 12,
				}}
			></div>

			<div onMouseDown={onMouseDown} onMouseUp={onMouseUp} onMouseMove={onMouseMove} style={{ width: 300, height: 300, position: "relative", border: "3px solid green" }}>
				<div
					ref={pointArr}
					style={{
						position: "absolute",
						fontSize: 50,
						textAlign: "center",
						display: "flex",
						justifyContent: "center",
						alignContent: "center",
						left: "0px",
						right: "0px",
						top: "0px",
						bottom: "0px",
						margin: "auto",
						height: 60,
						width: 60,
						transform: `rotate(${deg - 90}deg)`,
					}}
				>
					<span></span>
				</div>
			</div> 
		</>
	);
}

export default App;