Commit e5688af8 authored by zhushengjie's avatar zhushengjie

封装圆环组件

parent 5c5375fe
.Ring {
display: flex;
// align-items: center;
// justify-content: center;
}
.RingContent {
position: relative;
}
.sectionRing {
display: flex;
align-items: center;
justify-content: center;
}
.bar {
top: 0;
left: 0;
display: flex;
position: absolute;
align-items: center;
justify-content: center;
background-color: '#fff';
box-shadow: 0 5rpx 12rpx rgba(0, 0, 0, 0.6);
}
.colorThum {
display: flex;
position: absolute;
align-items: center;
justify-content: center;
color: white;
font-size: 16px;
font-weight: 700;
}
\ No newline at end of file
import React, { useState, useCallback } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import { utils } from '@ray-js/panel-sdk';
import { useAtomValue, useSetAtom } from 'jotai';
import { ColorData, colorDataAtom, selectColorDataAtom } from '@/atoms';
import { View, Text, Slider, Image, usePageEvent } from '@ray-js/ray';
import { View, Image } from '@ray-js/ray';
import styles from './index.module.less';
import { Props } from './index.type';
import imgs from '../../res';
import imgs from './colorBg.png';
const { hsv2rgbString } = utils;
// 平移法
const HubColorCircle: React.FC<Props> = props => {
const dpState = props.DpStateData;
const codeMap = props.DpCodesMap;
const setColorStateAtom = useSetAtom(colorDataAtom);
const colorStateInAtom = useAtomValue(selectColorDataAtom);
const dataStates = {
// 元素偏移
translateX: 0,
translateY: 0,
// 点击时的小球位置
locationX: 0,
locationY: 0,
};
const ColourHeader: React.FC<Props> = props => {
const data = {
radius: 135, // 容器半径
innerRadius: 76, // 内半径
......@@ -26,78 +28,49 @@ const HubColorCircle: React.FC<Props> = props => {
colorThumRadius: 76 - 5,
cx: 135 - 27.5, // 圆心相对父容器位置
cy: 135 - 27.5, // 圆心相对父容器位置
cx: 135 - 27.5, // 小球在圆心时相对父容器left位置
cy: 135 - 27.5, // 小球在圆心时相对父容器top位置
fixedLength: 135 - (135 - 76) * 0.5, // 可拖动圆球至原点的固定距离(令圆球始终在在色环中居中)
containerOffsetX: 135 - 27.5,
containerOffsetY: (135 - 76) * 0.5 - 27.5 * 0.5,
mouseMove: {
// 父元素偏移
x: 0,
y: 0,
},
location: {
// 点击时的小球位置
x: 0,
y: 0,
},
disabled: false,
RingBackground: imgs.colorBg,
hue: 90, // 色相
};
const atomState = {
// 父元素偏移
mouseMoveX: 0,
mouseMoveY: 0,
// 点击时的小球位置
locationX: 0,
locationY: 0,
};
const sectionRingRef = useRef(null);
const hueToColor = hue => {
return hsv2rgbString(hue, 1000, 1000);
const hueToColor = (hue: number, temp = 1000, bright = 1000, a = 1) => {
const _hue = Math.abs(hue) % 360;
return hsv2rgbString(_hue, temp / 10, bright / 10, a);
};
const [translateX, setTranslateX] = useState(0);
const [translateY, setTranslateY] = useState(0);
const [nowColor, setNowColor] = useState(hueToColor(data.hue));
const [hueState, sethueState] = useState(data.hue);
const getCoordByHue = (hue: number) => {
const getTransLateByHue = (hue: number) => {
const rad = ((360 - hue) * Math.PI) / 180;
const x = data.cx + data.fixedLength * Math.cos(rad);
const y = data.cy + data.fixedLength * Math.sin(rad);
return { x, y };
};
const handleBarTouch = e => {
atomState.locationX = e.origin.currentTarget.offsetLeft;
atomState.locationY = e.origin.currentTarget.offsetTop;
setColorStateAtom(atomState);
const [translateX, setTranslateX] = useState(getTransLateByHue(props.hue).x);
const [translateY, setTranslateY] = useState(getTransLateByHue(props.hue).y);
const [hueState, sethueState] = useState(props.hue);
console.log(data.location, 'touch');
atomState.mouseMoveX = e.origin.currentTarget.offsetLeft;
atomState.mouseMoveY = e.origin.currentTarget.offsetTop;
const handleBarTouch = (e: any) => {
e.origin.stopPropagation();
dataStates.locationX = e.origin.touches[0].clientX;
dataStates.locationY = e.origin.touches[0].clientY;
props.onBarTouch && props.onBarTouch();
};
const getHueByCoord = (dx: number, dy: number) => {
const getHueByTransLate = (LX: number, LY: number) => {
// 0 ~ 2π
const rad = getRadianByCoord(dx, dy);
const rad = getRadianByTransLate(LX, LY);
return (rad * 180) / Math.PI;
};
const getRadianByCoord = (dx: number, dy: number) => {
const getRadianByTransLate = (LX: number, LY: number) => {
const { thumbRadius } = data;
// 相对中心点的坐标
const xCenter = dx - data.cx - thumbRadius;
const yCenter = dy - data.cy - thumbRadius;
// 圆心相对中心点的坐标
const xCenter = LX - data.cx - thumbRadius;
const yCenter = LY - data.cy - thumbRadius;
let rad = Math.atan2(yCenter, xCenter);
if (xCenter > 0 && yCenter > 0) rad = Math.PI * 2 - rad;
......@@ -109,33 +82,47 @@ const HubColorCircle: React.FC<Props> = props => {
return rad;
};
const handleMove = e => {
// 最近一次的移动路程,dx的正负决定了移动的正负
const { locationX, locationY } = colorStateInAtom;
const dx = e.origin.touches[0].clientX - locationX;
const dy = e.origin.touches[0].clientY - locationY;
const hue = Math.round(getHueByCoord(dx, dy));
const { x = 0, y = 0 } = getCoordByHue(hue);
const handleMove = (e: any) => {
e.origin.stopPropagation();
// 累计移动路程,LX的正负决定了移动的正负
const { locationX, locationY } = dataStates;
const LX = e.origin.touches[0].clientX - locationX + dataStates.translateX;
const LY = e.origin.touches[0].clientY - locationY + dataStates.translateY;
const hue = Math.round(getHueByTransLate(LX, LY));
const { x = 0, y = 0 } = getTransLateByHue(hue);
const color = hueToColor(hue);
setTranslateX(x);
setTranslateY(y);
setNowColor(color);
sethueState(hue);
props.onBarMove && props.onBarMove(hue, color);
};
const handleEnd = () => {
dataStates.translateX = translateX;
dataStates.translateY = translateY;
props.onBarMoveEnd && props.onBarMoveEnd(hueState);
};
const handleRingClick = e => {
console.log(e, sectionRingRef);
// 因为无法获取到指定元素的位置,所以无法实现让小球处于点击位置
};
return (
<View className={styles.header}>
<View className={styles.Ring}>
<View
className={styles.circleContent}
className={styles.RingContent}
style={{ width: `${data.radius * 2}px`, height: `${data.radius * 2}px` }}
>
{/* 圆环 */}
<View
ref={sectionRingRef}
className={styles.sectionRing}
style={{ width: `${data.radius * 2}px`, height: `${data.radius * 2}px` }}
onClick={handleRingClick}
>
<Image
style={{
......@@ -143,7 +130,7 @@ const HubColorCircle: React.FC<Props> = props => {
height: `${data.radius * 2}px`,
borderRadius: '50%',
}}
src={imgs.colorBg}
src={imgs}
/>
</View>
......@@ -157,7 +144,7 @@ const HubColorCircle: React.FC<Props> = props => {
width: `${data.colorThumRadius * 2}px`,
height: `${data.colorThumRadius * 2}px`,
borderRadius: '50%',
backgroundColor: nowColor,
backgroundColor: hueToColor(hueState, props.temp, props.bright),
}}
>
Angle:{hueState}°
......@@ -167,8 +154,6 @@ const HubColorCircle: React.FC<Props> = props => {
<View
className={styles.bar}
style={{
top: `${data.containerOffsetY}px`,
left: `${data.containerOffsetX}px`,
width: `${data.thumbRadius * 2}px`,
height: `${data.thumbRadius * 2}px`,
borderRadius: '50%',
......@@ -177,13 +162,14 @@ const HubColorCircle: React.FC<Props> = props => {
}}
onTouchStart={handleBarTouch}
onTouchMove={handleMove}
onTouchEnd={handleEnd}
>
<View
style={{
width: `${data.thumbInnerRadius * 2}px`,
height: `${data.thumbInnerRadius * 2}px`,
borderRadius: '50%',
backgroundColor: nowColor,
backgroundColor: 'transparent',
}}
/>
</View>
......@@ -192,4 +178,4 @@ const HubColorCircle: React.FC<Props> = props => {
);
};
export default HubColorCircle;
export default ColourHeader;
export interface Props {
hue: number;
temp?: number;
bright?: number;
onBarTouch?: () => void;
onBarMove?: (hue: number, color: any) => void;
onBarMoveEnd?: (hue: number) => void;
}
......@@ -6,43 +6,9 @@
.header {
flex: 1;
// display: flex;
// justify-content: center;
// align-items: center;
.circleContent {
position: relative;
z-index: 30;
left: 100px;
top: 50px;
background-image: url(./colorBg.png);
border: none;
.circle {
position: absolute;
z-index: 10;
left: 0;
top: 0;
border-radius: 50%;
border: 10px solid #ffffff;
display: flex;
justify-content: center;
align-items: center;
color: #ffffff;
font-size: 20px;
-webkit-user-select: none;
}
.circleBar {
position: absolute;
z-index: 50;
border-radius: 50%;
background-color: transparent;
border: 4rpx solid rgba(255, 255, 255, 1);
cursor: pointer;
box-shadow: 0 5rpx 12rpx rgba(0, 0, 0, 0.6);
}
}
display: flex;
justify-content: center;
align-items: center;
}
.panel {
......@@ -124,36 +90,3 @@
border-radius: 20rpx;
background-color: #1f1f1f;
}
// .header {
// display: flex;
// // align-items: center;
// // justify-content: center;
// }
// .circleContent {
// position: relative;
// }
// .sectionRing {
// display: flex;
// align-items: center;
// justify-content: center;
// }
// .bar {
// display: flex;
// position: absolute;
// align-items: center;
// justify-content: center;
// background-color: '#fff';
// box-shadow: 0 5rpx 12rpx rgba(0, 0, 0, 0.6);
// }
// .colorThum {
// display: flex;
// position: absolute;
// align-items: center;
// color: white;
// font-size: 16px;
// font-weight: 700;
// }
import React, { useState } from 'react';
import { utils } from '@ray-js/panel-sdk';
import { hsvToDpdata, dpdataToHsv } from '@/utils';
import { useAtomValue, useSetAtom } from 'jotai';
import { ColorData, colorDataAtom, selectColorDataAtom } from '@/atoms';
import { View, Text, Slider, Image, Modal, Button } from '@ray-js/ray';
import { View, Text, Slider, Image, Modal } from '@ray-js/ray';
import styles from './index.module.less';
import { Props } from './index.type';
import imgs from '../../res';
import HubColorHeader from '../HubColorHeader';
type colorItem = Record<string, any>;
......@@ -14,40 +13,12 @@ const HubCircle: React.FC<Props> = props => {
const dpState = props.DpStateData;
const codeMap = props.DpCodesMap;
const setColorStateAtom = useSetAtom(colorDataAtom);
const colorStateInAtom = useAtomValue(selectColorDataAtom);
const { hsv2rgb, rgb2hex, hsv2rgbString } = utils;
const { hsv2rgbString } = utils;
const Hue = dpdataToHsv(dpState.colour_data)[0];
const Temp_value = dpdataToHsv(dpState.colour_data)[1];
const Bright_value = dpdataToHsv(dpState.colour_data)[2];
// document.getElementById;
const dataSourse = {
containerWidth: 400,
containerStyle: {
width: '200px',
height: '200px',
backgroundSize: '200px 200px',
},
containerOffset: {
// 容器位置
x: 100,
y: 150,
},
mouse_offset: { x: 0, y: 0 },
circle_r: 100, // 圆形外半径(容器宽度/2)
circle_b: 42, // 圆形边距
bar_r: 21, // 小球半径(圆形边距/2)
bar_c_r: 100 - 21, // 小球轨迹半径(圆形边距/2)
hue: 270,
};
const hueToColor = (h: number, s = 100, v = 100, a = 1) => {
return hsv2rgbString(h, s, v, a);
};
......@@ -68,43 +39,6 @@ const HubCircle: React.FC<Props> = props => {
nowColor: hueToColor(Hue), // 当前颜色
});
const circleLocationOrigin = (hue: number) => {
let angel = hue - 90;
let rad;
let left = 79;
let top = 0;
if (angel > 180) {
angel = -(360 - angel);
}
if (angel < 0) {
rad = (-angel * Math.PI) / 180;
const y = dataSourse.bar_c_r * Math.cos(rad);
const x = dataSourse.bar_c_r * Math.sin(rad);
left += x;
top = dataSourse.circle_r - y - dataSourse.bar_r;
}
if (angel > 0) {
rad = (angel * Math.PI) / 180;
const x = dataSourse.bar_c_r * Math.sin(rad);
const y = dataSourse.bar_c_r * Math.cos(rad);
left -= x;
top = dataSourse.circle_r - y - dataSourse.bar_r;
}
return {
left,
top,
};
};
const [circleLocation, setCircleLocation] = useState({
// 小球位置
left: circleLocationOrigin(Hue).left,
top: circleLocationOrigin(Hue).top,
});
const toParseInt = (value: number): number => {
return Math.floor(value);
};
......@@ -142,7 +76,7 @@ const HubCircle: React.FC<Props> = props => {
return;
}
const h = dataState.hue;
const nowColor = hueToColor(h, dataState.temp_value / 10, dataState.bright_value / 9.9);
const nowColor = hueToColor(h, dataState.temp_value / 10, dataState.bright_value / 10);
setDataState({
...dataState,
......@@ -174,87 +108,9 @@ const HubCircle: React.FC<Props> = props => {
setColorArr(value);
};
const handleBarTouch = (e: any) => {
e.origin.stopPropagation();
setColorStateAtom({ ifDrag: true });
// 移动距离:小球的坐标位置-容器的偏移位置-小球相对容器的偏移位置-小球半径
const mouse_offset_x =
e.origin.touches[0].clientX -
dataSourse.containerOffset.x -
e.origin.target.offsetLeft -
dataSourse.bar_r;
const mouse_offset_y = -(
e.origin.touches[0].clientY -
dataSourse.containerOffset.y -
e.origin.target.offsetTop -
dataSourse.bar_r
);
dataSourse.mouse_offset.x = mouse_offset_x;
dataSourse.mouse_offset.y = mouse_offset_y;
};
const handleBarMove = (e: any) => {
e.origin.stopPropagation();
const event_offset_x =
e.origin.touches[0].clientX -
dataSourse.mouse_offset.x -
dataSourse.containerOffset.x -
dataSourse.circle_r;
const event_offset_y = -(
e.origin.touches[0].clientY -
dataSourse.mouse_offset.y -
dataSourse.containerOffset.y -
dataSourse.circle_r
);
const radian = Math.atan2(event_offset_y, event_offset_x);
const x = Math.cos(radian) * (dataSourse.circle_r - dataSourse.circle_b / 2);
const y = Math.sin(radian) * (dataSourse.circle_r - dataSourse.circle_b / 2);
const left = x - dataSourse.bar_r + dataSourse.circle_r;
const top = dataSourse.circle_r - (y + dataSourse.bar_r);
if (!colorStateInAtom.ifDrag) {
return;
}
updateContent(radian);
setCircleLocation({
...circleLocation,
left,
top,
});
};
const updateContent = (radian: number) => {
let angle = radian * (180 / Math.PI); // -180 ~ 180
if (angle >= -180 && angle <= 90) {
angle = 90 - angle;
} else {
angle = 360 - (angle - 90);
}
// 0 ~ 360
// 转为逆时针顺序
let hue = Math.floor(360 - angle + 90);
if (hue >= 360) {
hue -= 360;
}
const nowColor = hueToColor(hue, dataState.temp_value / 10, dataState.bright_value / 9.9);
setDataState({
...dataState,
hue,
nowColor,
});
};
const handleStateChange = (type: string, e: any, ifEnd?: string) => {
if (type === 'temp_value') {
const temp_value = e.value;
const nowColor = hueToColor(dataState.hue, temp_value / 10, dataState.bright_value / 9.9);
const dpValue = hsvToDpdata(dataState.hue, temp_value, dataState.bright_value);
if (ifEnd) {
props.onSetDpState('colour_data', dpValue);
......@@ -263,12 +119,10 @@ const HubCircle: React.FC<Props> = props => {
setDataState({
...dataState,
temp_value,
nowColor,
});
}
if (type === 'bright_value') {
const bright_value = e.value;
const nowColor = hueToColor(dataState.hue, dataState.temp_value / 10, bright_value / 9.9);
const dpValue = hsvToDpdata(dataState.hue, dataState.temp_value, bright_value);
if (ifEnd) {
props.onSetDpState('colour_data', dpValue);
......@@ -277,7 +131,6 @@ const HubCircle: React.FC<Props> = props => {
setDataState({
...dataState,
bright_value,
nowColor,
});
}
};
......@@ -286,42 +139,20 @@ const HubCircle: React.FC<Props> = props => {
setModalShow(flag);
};
const handleBarEnd = () => {
setColorStateAtom({ ifDrag: false });
const dpValue = hsvToDpdata(dataState.hue, dataState.temp_value, dataState.bright_value);
const handleBarEnd = (hue: number) => {
const dpValue = hsvToDpdata(hue, dataState.temp_value, dataState.bright_value);
props.onSetDpState('colour_data', dpValue);
};
return (
<View className={styles.box}>
<View className={styles.header}>
<View className={styles.circleContent} id="circleContent" style={dataSourse.containerStyle}>
<View
className={styles.circle}
style={{
width: '100px',
height: '100px',
left: '50px',
top: '50px',
backgroundColor: dataState.nowColor,
}}
>
{dataState.hue}°
</View>
<View
className={styles.circleBar}
style={{
left: `${circleLocation.left}px`,
top: `${circleLocation.top}px`,
width: `${dataSourse.bar_r * 2}px`,
height: `${dataSourse.bar_r * 2}px`,
backgroundColor: dataState.nowColor,
}}
onTouchStart={handleBarTouch}
onTouchMove={handleBarMove}
onTouchEnd={handleBarEnd}
/>
</View>
<HubColorHeader
hue={Hue}
temp={dataState.temp_value}
bright={dataState.bright_value}
onBarMoveEnd={handleBarEnd}
/>
</View>
<View className={styles.panel}>
<View className={styles.configList}>
......@@ -333,13 +164,13 @@ const HubCircle: React.FC<Props> = props => {
min={0}
max={1000}
step={1}
value={dpState.temp_value}
value={dataState.temp_value}
onChange={e => handleStateChange('temp_value', e, 'end')}
onChanging={e => handleStateChange('temp_value', e)}
/>
</View>
<Text className={styles.SliderValue}>
{toParseInt((dpState.temp_value / 1000) * 100)}%
{toParseInt((dataState.temp_value / 1000) * 100)}%
</Text>
</View>
<View className={styles.configList}>
......@@ -353,7 +184,7 @@ const HubCircle: React.FC<Props> = props => {
step={1}
value={dataState.bright_value}
onChange={e => handleStateChange('bright_value', e, 'end')}
onChanging={e => handleStateChange('temp_value', e)}
onChanging={e => handleStateChange('bright_value', e)}
/>
</View>
<Text className={styles.SliderValue}>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment