颜色编辑器

import React from 'react'

import "./index.css";

import { IConfig, SampleObject, gradientObject } from "../common/types";

interface IProps {

config: IConfig,

left: number,

top: number,

hideContainer: () => void,

cancel?: () => void,

confirm?: (v: gradientObject[]) => void,

colorChange?: (v: gradientObject[]) => void,

}

interface IState {

samples: SampleObject[],

opacity: number | string,

domOne: {

isMoving: boolean

},

domTwo: {

isMoving: boolean

},

domThree: {

isMoving: boolean

},

domSample: {

isMoving: boolean

},

pureValue: string,

type: 'linear-gradient',

angle: number,

//操作的浮标下标,因为要给浮标设置颜色

sampleIndex: number,

stepVal: number | string,

stepScaleVal: number | null | string

}

type rgb = {

r?: number,

g?: number,

b?: number

}

export class ColorContainer extends React.Component<IProps, IState> {

rgba = { r: 0, g: 0, b: 0, a: 1 }

hsb = { h: 0, s: 100, b: 100 }

Initial = null; //默认的初始颜色

setTime = null; //延时器timer

throttle = null; //节流timer

dotOneRef = React.createRef<HTMLDivElement>(); // DotTwo节点

dotTwoRef = React.createRef<HTMLDivElement>(); // DotTwo节点

dotThreeRef = React.createRef<HTMLDivElement>(); // DotThree节点

colorPickerContainerRef = React.createRef<HTMLDivElement>(); // ColorPickerContainer节点

PanelContainerRef = React.createRef<HTMLDivElement>() // PanelContainer节点

sampleRef = React.createRef<HTMLDivElement>() // PanelContainer节点

domTwoDiscolorationRef = React.createRef<HTMLDivElement>()

domThreeDiscolorationRef = React.createRef<HTMLDivElement>()

constructor(props:IProps ) {

super(props)

this.state = {

domOne: {

isMoving: false

},

domTwo: {

isMoving: false,

},

domThree: {

isMoving: false,

},

domSample: {

isMoving: false,

},

opacity: 0, //点击并拖动透明设置条设置

// sample上面的颜色数组,如果不是纯色,最终返回的结果从samples遍历获取

samples:[

{

color: '#667db8ed',

step: 0.2

},{

color: '#6f66b8ed',

step: 0.5

}

],

pureValue: '', //#aaaaaaaa,如果是纯色,最终返回的结果是这个

//类型,线性渐变还是径向渐变

//linear-gradient线性渐变

//radial-gradient径向渐变

type: 'linear-gradient',

//角度

angle: 90,

//操作的浮标下标,因为要给浮标设置颜色

sampleIndex: 0,

stepVal: 0,

stepScaleVal: 0

}

}

componentDidMount() {

// 读取props里面的数据并设置到state中

const { config } = this.props

this.setState({

samples: JSON.parse(JSON.stringify(config.list))

})

// 获取第一个浮标的透明度并设置

const { samples } = this.state

let sampleItemOpacity = + (this.hex2int(samples[0].color.slice(-2)) / 255).toFixed(2)

this.setState({

opacity: sampleItemOpacity,

stepVal: (+samples[0].step).toFixed(3),

stepScaleVal: config.stepScale == null ? null : this.numberMul(samples[0].step, config.stepScale)

})

this.dotThreeRef.current!.style.left = (sampleItemOpacity * 216 - 7.5) + 'px'

}

clickPanel = (evt: any) => {

const { domOne } = this.state

if( domOne.isMoving ) {

return;

}

evt = evt.nativeEvent

let posX = evt.clientX - this.colorPickerContainerRef.current!.offsetLeft - 7.5 - this.props.left

let posY = evt.clientY - this.colorPickerContainerRef.current!.offsetTop - 7.5 - this.props.top

this.dotOneRef.current!.style.top = posY + 'px'

this.dotOneRef.current!.style.left = posX + 'px'

this.panelCalculation(posX, posY)

}

changePickedColor = (evt: any) => {

this.setState({

domOne: {

...this.state.domOne,

isMoving: true

}

})

evt.stopPropagation()

evt = evt.nativeEvent

// let dotOneTop = evt.offsetY > 0 ? evt.offsetY : 0;

// let dotOneLeft = evt.offsetX > 0 ? evt.offsetX : 0;

document.onmousemove = (e) => {

e.preventDefault();

let oneTop = e.clientY - (this.colorPickerContainerRef.current!.offsetTop) - this.props.top - 7.5

oneTop = oneTop <= -7.5 ? -7.5 : oneTop;

oneTop = oneTop >= 116.5 ? 116.5 : oneTop;

let oneLeft = e.clientX - (this.colorPickerContainerRef.current!.offsetLeft) - this.props.left -7.5

oneLeft = oneLeft <= -7.5 ? -7.5 : oneLeft;

oneLeft = oneLeft >= 209.5 ? 209.5 : oneLeft;

this.dotOneRef.current!.style.top = oneTop + "px";

this.dotOneRef.current!.style.left = oneLeft + "px";

// 计算面板中的dot1所在位置的颜色

this.panelCalculation(

(e.clientX - this.colorPickerContainerRef.current!.offsetLeft - 7.5 ),

(e.clientY - this.colorPickerContainerRef.current!.offsetTop -7.5 ),

)

}

document.onmouseup = () => {

document.onmousemove = null;

document.onmouseup = null;

this.setState({

domOne: {

...this.state.domOne,

isMoving: false

}

})

}

}

panelCalculation = (x: number, y: number) => {

let MaxLeft = Math.max(0, Math.min(x, 216));

let MaxTop = Math.max(0, Math.min(y, 124));

this.hsb.s = 100 * MaxLeft / 216;

this.hsb.b = 100 * (124 - MaxTop) / 124;

this.changeColor();

}

// 按下并移动按钮,移动DomTwo位置,并改变panel颜色

changeDomTwoPosition = (event: any) => {

this.setState({

domTwo: {

...this.state.domTwo,

isMoving: true

}

})

event.stopPropagation()

event.nativeEvent.stopImmediatePropagation()

let dotTwoLeft = event.nativeEvent.offsetX > 0 ? event.nativeEvent.offsetX : 0;

let domTowEle = this.dotTwoRef.current!;

let colorPickerContainerDom = this.colorPickerContainerRef.current!;

document.onmousemove = (evt) => {

evt.preventDefault();

evt.stopImmediatePropagation()

let twoLeft = evt.clientX - (colorPickerContainerDom.offsetLeft + dotTwoLeft) - this.props.left

// let twoLeft = evt.offsetX - colorPickerContainerDom.offsetLeft

twoLeft = twoLeft <= -7.5 ? -7.5 : twoLeft

twoLeft = twoLeft >= 209.5 ? 209.5 : twoLeft

// 设置滑轮的位置和panel的背景色

domTowEle.style.left = twoLeft + 'px'

this.changePanelBgColor(evt.clientX - colorPickerContainerDom.offsetLeft - this.props.left)

}

document.onmouseup = () => {

document.onmousemove = null

document.onmouseup = null

this.setState({

domTwo: {

...this.state.domTwo,

isMoving: false

}

})

}

}

clickDiscoloration = (evt: any) => {

if( evt.target !== this.domTwoDiscolorationRef.current ) {

return;

}

const { domTwo } = this.state

if( domTwo.isMoving ) {

return;

}

evt = evt.nativeEvent

this.dotTwoRef.current!.style.left = (evt.offsetX - 7.5) + "px";

this.changePanelBgColor(evt.offsetX);

}

//参数x是domTow移动的位置

changePanelBgColor = (x: number) => {

let Left = Math.max(0, Math.min(x, 216));

this.hsb.h = 360 * Left / 216;

let rgb = this.hsbToRgb({

h: this.hsb.h,

s: 100,

b: 100

});

this.PanelContainerRef.current!.style.backgroundColor = 'rgba(' + rgb.r + ',' + rgb.g + ',' +

rgb.b + ',' + this.rgba.a + ')';

this.changeColor();

}

// 点击透明带设置透明度

setOpacity = ( evt: any ) => {

if( evt.target !== this.domThreeDiscolorationRef.current ) {

return

}

const { domThree } = this.state

if( domThree.isMoving ) {

return;

}

evt.stopPropagation()

evt = evt.nativeEvent

evt.stopImmediatePropagation()

this.dotThreeRef.current!.style.left = (evt.offsetX - 7.5) + "px";

this.setState({

opacity: (evt.offsetX / 216).toFixed(2)

},()=>{

this.changeColor()

})

}

// 点击并拖动透明度设置条

changeDomThreePosition = (event: any) => {

this.setState({

domThree: {

...this.state.domThree,

isMoving: true

}

})

event.stopPropagation()

event.nativeEvent.stopImmediatePropagation()

let dotThreeLeft = event.nativeEvent.offsetX > 0 ? event.nativeEvent.offsetX : 0;

let domThreeEle = this.dotThreeRef.current

let colorPickerContainerDom = this.colorPickerContainerRef.current

document.onmousemove = (evt) => {

evt.preventDefault();

evt.stopImmediatePropagation()

let threeLeft = evt.clientX - (colorPickerContainerDom!.offsetLeft + dotThreeLeft) - this.props.left

// let threeLeft = evt.offsetX - dotThreeLeft

threeLeft = threeLeft <= -7.5 ? -7.5 : threeLeft

threeLeft = threeLeft >= 209.5 ? 209.5 : threeLeft

// 设置滑轮的位置和panel的背景色

domThreeEle!.style.left = threeLeft + 'px'

this.setState({

opacity: ((threeLeft + 7.5) / 216).toFixed(2)

},()=>{

this.changeColor()

})

}

document.onmouseup = () => {

document.onmousemove = null

document.onmouseup = null

this.setState({

domThree: {

...this.state.domThree,

isMoving: false

}

})

}

}

// 点击sample后添加一个sample

addSample = ( evt: any ) => {

if( evt.target !== this.sampleRef.current ) {

return

}

const { domSample } = this.state

if( domSample.isMoving ) {

return;

}

evt = evt.nativeEvent

let posX = evt.offsetX - 5

let samples = JSON.parse(JSON.stringify( this.state.samples ))

samples.push({

color: '#ff0000ff',

step: posX / 216

})

this.setState({

samples

})

}

// 双击sample以删除

removeSample = (idx: number) => {

let samples = JSON.parse(JSON.stringify( this.state.samples ))

const { config } = this.props

if( samples.length < 2 ) {

return console.error('浮标不能少于2个')

}

samples.splice(idx, 1)

this.setState({

samples,

sampleIndex: 0,

stepVal: (+samples[0].step).toFixed(3),

stepScaleVal: config.stepScale == null ? null : this.numberMul(samples[0].step, config.stepScale)

})

}

// 改变旋转角度,参数的意思是要改多少

// changeAngle = (chanedAngle) => {

// const { angle } = this.state

// if( typeof chanedAngle === 'number' ) {

// return this.setState({

// angle: angle + chanedAngle

// })

// }

// // 如果chanedAngle是一个事件

// if( chanedAngle.code !== 'Enter' ) {

// return

// }

// let val = + (chanedAngle.nativeEvent.srcElement.value)

// this.setState({

// angle: angle + val

// })

// }

// 点击并移动sample浮块

// 参数是事件对象和sample数组的下标

moveSample = (event: any, idx: number) => {

// 获取当前sample的透明度并设置

const { samples } = this.state

const { config } = this.props

let sampleItemOpacity = + (this.hex2int(samples[idx].color.slice(-2)) / 255).toFixed(2)

this.setState({

domSample: {

...this.state.domSample,

isMoving: true

},

sampleIndex: idx,

opacity: sampleItemOpacity,

stepVal: samples[idx].step.toFixed(3),

stepScaleVal: config.stepScale == null ? null : this.numberMul(parseFloat(samples[idx].step.toFixed(3)), config.stepScale).toFixed(3)

})

this.dotThreeRef.current!.style.left = (sampleItemOpacity * 216 - 7.5) + 'px'

event.stopPropagation()

event.nativeEvent.stopImmediatePropagation()

let left = event.nativeEvent.offsetX > 0 ? event.nativeEvent.offsetX : 0;

let colorPickerContainerDom = this.colorPickerContainerRef.current

document.onmousemove = (evt) => {

evt.preventDefault();

evt.stopImmediatePropagation()

let sampleLeft = evt.clientX - (colorPickerContainerDom!.offsetLeft + left) - this.props.left

sampleLeft = sampleLeft <= 0 ? 0 : sampleLeft

sampleLeft = sampleLeft >= 216 ? 216 : sampleLeft

let samples = JSON.parse(JSON.stringify( this.state.samples ))

samples[idx].step = sampleLeft / 216

this.setState({

samples,

stepVal: samples[idx].step.toFixed(3),

stepScaleVal: config.stepScale == null ? null : this.numberMul(samples[idx].step, config.stepScale).toFixed(3)

})

}

document.onmouseup = () => {

document.onmousemove = null

document.onmouseup = null

this.setState({

domSample: {

...this.state.domSample,

isMoving: false

},

})

}

}

changeColor = () => {

let rgb = this.hsbToRgb(this.hsb);

this.rgba.r = rgb.r;

this.rgba.g = rgb.g;

this.rgba.b = rgb.b;

const { opacity } = this.state

const { samples , sampleIndex} = this.state

const tempSamples = JSON.parse(JSON.stringify(samples))

tempSamples[sampleIndex].color = this.rgbToHex(rgb, opacity)

this.setState(()=>({

samples: tempSamples

}))

}

// // 点击按钮,改变颜色的类型(径向,渐变,纯色)

// changeType = (type) => {

// this.setState({

// type

// })

// }

//颜色类型转换

hsbToRgb = (hsb: any) => {

var rgb:rgb = {};

var h = hsb.h;

var s = hsb.s * 255 / 100;

var v = hsb.b * 255 / 100;

if (s === 0) {

rgb.r = rgb.g = rgb.b = v;

} else {

var t1 = v;

var t2 = (255 - s) * v / 255;

var t3 = (t1 - t2) * (h % 60) / 60;

if (h === 360) h = 0;

if (h < 60) {

rgb.r = t1;

rgb.b = t2;

rgb.g = t2 + t3

} else if (h < 120) {

rgb.g = t1;

rgb.b = t2;

rgb.r = t1 - t3

} else if (h < 180) {

rgb.g = t1;

rgb.r = t2;

rgb.b = t2 + t3

} else if (h < 240) {

rgb.b = t1;

rgb.r = t2;

rgb.g = t1 - t3

} else if (h < 300) {

rgb.b = t1;

rgb.g = t2;

rgb.r = t2 + t3

} else if (h < 360) {

rgb.r = t1;

rgb.g = t2;

rgb.b = t1 - t3

} else {

rgb.r = 0;

rgb.g = 0;

rgb.b = 0

}

}

return {

r: Math.round(rgb.r),

g: Math.round(rgb.g),

b: Math.round(rgb.b)

};

}

hex2int = (hex: any) => {

var len = hex.length, a = new Array(len), code;

for (var i = 0; i < len; i++) {

code = hex.charCodeAt(i);

if (48<=code && code < 58) {

code -= 48;

} else {

code = (code & 0xdf) - 65 + 10;

}

a[i] = code;

}

return a.reduce(function(acc, c) {

acc = 16 * acc + c;

return acc;

}, 0);

}

rgbToHex = ({ r,g,b }:{r: number, g: number, b: number}, opacity: number | string | undefined = 0) => {

let hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)

let HexOpacity = ((opacity as number) * 255).toString(16).slice(0,2)

if( (+opacity) === 0 ) {

HexOpacity = '00'

}

// 如果最后是 .如a.,b.,要改为a0,b0

if( HexOpacity.indexOf('.') > -1 ) {

HexOpacity = HexOpacity.slice(0,-1) + '0'

}

return hex + HexOpacity

}

// 计算数字相乘

numberMul(arg1: number, arg2: number){

var m=0,s1=arg1.toString(),s2=arg2.toString();

try{

m+=s1.split(".")[1].length;

}catch(e){}

try{

m+=s2.split(".")[1].length;

}catch(e){}

return Number(s1.replace(".",""))*Number(s2.replace(".",""))/Math.pow(10,m);

}

// 计算数字相除

division(arg1: number, arg2: number){

var t1 = 0, t2 = 0, r1, r2;

try {

t1 = (arg1 + "").split(".")[1].length;

} catch (e) { }

try {

t2 = arg2.toString().split(".")[1].length;

} catch (e) { }

r1 = Number((arg1 + "").replace(".", ""));

r2 = Number(arg2.toString().replace(".", ""));

//放大倍数后两个数相除 后,乘以两个小数位数长度相减后的10的次幂

var money = this.numberMul((r1 / r2),Math.pow(10, t2 - t1));

//保留2个小数位数

return money;

}

// 生成最终的颜色样式

generateStyle = () => {

const { type, samples, angle, pureValue } = this.state

if( samples.length === 0 ) {

return 'none'

}

let getStyle = {

'radial-gradient':() => {

let style = `${type}(circle,`

samples.forEach(sample=>{

style += `${sample.color} ${sample.step * 100}%,`

})

style = style.slice(0,-1) + ')'

return style

},

'linear-gradient':() => {

let style = `${type}(${angle}deg,`

samples.forEach(sample=>{

style += `${sample.color} ${sample.step * 100}%,`

})

style = style.slice(0,-1) + ')'

return style

},

'pure':() => {

return pureValue

}

}

return getStyle[type]()

}

changeStep = ( e: any ) => {

let iptVal = e.target.value

const samples = JSON.parse(JSON.stringify(this.state.samples))

const { sampleIndex } = this.state

const { config } = this.props

samples[sampleIndex].step = iptVal

this.setState({

samples,

stepVal: iptVal,

stepScaleVal: config.stepScale == null ? null : this.numberMul(iptVal, config.stepScale)

})

}

changeStepScale = ( e: any ) => {

const { stepScale } = this.props.config

let iptVal = e.target.value || 0

const samples = JSON.parse(JSON.stringify(this.state.samples))

const { sampleIndex } = this.state

samples[sampleIndex].step = this.division(iptVal, stepScale)

this.setState({

samples,

stepVal: this.division(iptVal, stepScale),

stepScaleVal: iptVal

})

}

// 和外面的交互

cancel = () => {

this.props.hideContainer()

this.props.cancel && this.props.cancel()

}

confirm = () => {

this.props.hideContainer()

const { samples } = this.state

let val = samples.map(item=>{

return {

step: +((+item.step).toFixed(3)),

color: item.color

}

})

this.props.confirm && this.props.confirm(val)

}

onColorChange = () => {

const { samples } = this.state

let val = samples.map(item=>{

return {

step: +((+item.step).toFixed(3)),

color: item.color

}

})

this.props.colorChange && this.props.colorChange(val)

}

render() {

// 浮标

const { samples, stepVal, stepScaleVal, sampleIndex: sampleIdx} = this.state

const { colorChange } = this.props

const sampleItems = samples.map((sample,sampleIndex)=>{

let left = (+216 * sample.step - 8).toFixed(3)

let borderBottomColor = 'black'

if( sampleIdx === sampleIndex ) {

borderBottomColor = 'red'

}

return (

<div className="sample-item" key={sampleIndex} style={{ left: left + 'px', borderBottomColor}} onMouseDown={( e )=> this.moveSample(e, sampleIndex) } onDoubleClick={ () => this.removeSample(sampleIndex) }></div>

)

})

// 最终生成的渐变色样式,用于展示

const generatedStyle = this.generateStyle();

colorChange && this.onColorChange()

return (

<div className="ColorPickerBox">

{/* <div className="TypeButtons">

<div className="btn" onClick={()=> this.changeType('linear-gradient') }>线性</div>

<div className="btn" onClick={()=> this.changeType('radial-gradient') }>径向</div>

<div className="btn empty" onClick={()=> this.changeType('pure') }>纯色</div>

</div> */}

<div className="sample" ref={this.sampleRef} onClick={ ( e ) => this.addSample(e) } style={{ background: generatedStyle }}>

{ sampleItems }

</div>

{/* 一些输入项 */}

<div className="InputsInteract">

<div className="InputsItem">

<div className="InputsItemLabel">步长</div>

<input className="InputsItemInput" value={ stepVal } onChange={ this.changeStep }/>

</div>

{

stepScaleVal != null &&

<div className="InputsItem">

<div className="InputsItemLabel">合计</div>

<input className="InputsItemInput" value={ stepScaleVal } onChange={ this.changeStepScale }/>

</div>

}

</div>

<div className="ColorPickerContainer" ref={this.colorPickerContainerRef}>

<div className="PanelContainer" ref={this.PanelContainerRef}>

<div className="panel" onClick={ this.clickPanel }>

<div className="variable">

<div className="DotOne" onMouseDown={this.changePickedColor} ref={this.dotOneRef}></div>

</div>

</div>

</div>

{/* 颜色选择条 */}

<div className="BarContainer">

<div className="Discoloration" onClick={ this.clickDiscoloration } ref={ this.domTwoDiscolorationRef }>

<div className="DotTwo" ref={this.dotTwoRef} onMouseDown={this.changeDomTwoPosition}></div>

</div>

</div>

{/* 透明度选择条 */}

<div className="OpacityBar">

<div className="Discoloration" onClick={ this.setOpacity } ref={ this.domThreeDiscolorationRef }>

<div className="DotThree" ref={ this.dotThreeRef } onMouseDown={this.changeDomThreePosition}></div>

</div>

</div>

{/* 操作按钮,删除或者确定 */}

<div className="Interact">

<div className="btn empty" onClick={ this.cancel }>取消</div>

<div className="btn" onClick={ this.confirm }>确定</div>

</div>

</div>

</div>

)

}

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值