Skip to content

Commit fa6d813

Browse files
committed
减少色彩空间转换造成的误差,优化取色拖动逻辑
1 parent e97f726 commit fa6d813

16 files changed

+163
-203
lines changed

packages/veui-theme-one/components/color-picker.less

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050

5151
.veui-color-hue-slider {
5252
.veui-slider-custom-track {
53-
background: linear-gradient(to left, #F00, #FF0, #0F0, #0FF, #00F, #F0F, #F00);
53+
background: linear-gradient(to right, #F00, #FF0, #0F0, #0FF, #00F, #F0F, #F00);
5454
}
5555
}
5656

packages/veui/src/components/ColorPicker/ColorPicker.vue

Lines changed: 4 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,14 @@
99
:width="shadeFieldSize[0]"
1010
:height="shadeFieldSize[1]"
1111
:ui="realUi"
12-
:hue="hsva.h"
13-
:saturation="hsva.s"
14-
:brightness="hsva.v"
15-
:alpha="hsva.a"
12+
:color="color"
13+
:hsv="hsv"
1614
/>
1715
<div class="veui-color-picker-main-panel-sliders">
18-
<veui-color-hue-slider :value="hsva.h"/>
16+
<veui-color-hue-slider :hsl="hsl"/>
1917
<veui-color-alpha-slider
2018
v-if="alpha"
21-
:hue="hsva.h"
22-
:saturation="hsva.s"
23-
:brightness="hsva.v"
24-
:value="hsva.a"
19+
:hsl="hsl"
2520
/>
2621
</div>
2722
</div>
@@ -37,7 +32,6 @@
3732
</template>
3833

3934
<script>
40-
import tinycolor from 'tinycolor2'
4135
import ColorSwatch from './ColorSwatch'
4236
import ui from '../../mixins/ui'
4337
import ColorHomer from './mixins/_ColorHomer'
@@ -57,42 +51,12 @@ export default {
5751
mixins: [ui, ColorHomer],
5852
data () {
5953
return {
60-
previousHsva: {},
6154
shadeFieldSizeMap: config.get('colorpicker.shadeFieldSize')
6255
}
6356
},
6457
computed: {
6558
shadeFieldSize () {
6659
return this.shadeFieldSizeMap[this.uiProps.size]
67-
},
68-
hsva () {
69-
let prevHsva = this.previousHsva
70-
let hsva = tinycolor(this.color).toHsv()
71-
72-
// fix
73-
if (prevHsva.h % 360 === hsva.h % 360) {
74-
// 因为色相是 360度循环的,360 被 tinycolor 转换成了 0,直接用的话会导致滑块跳变,所以这个特殊处理下
75-
hsva.h = prevHsva.h
76-
}
77-
if (tinycolor.equals(hsva, prevHsva)) {
78-
// 连续纯黑色情况下(SaturationBrightnessField底部区域),
79-
// 传出再传入的 hsv 不一样,导致信息丢失,这里恢复一下,不然取色圈会跳变
80-
// hsv(40, 0.001, 0.001) -> rgb(0, 0, 0) -> hsv(0, 0, 0)
81-
hsva.h = prevHsva.h
82-
hsva.s = prevHsva.s
83-
}
84-
if (hsva.s === 0 && prevHsva.s !== 0) {
85-
hsva.h = prevHsva.h
86-
}
87-
if (hsva.h === undefined) {
88-
hsva.h = prevHsva.h
89-
}
90-
91-
if (!this.alpha) {
92-
hsva.a = 1
93-
}
94-
95-
return hsva
9660
}
9761
}
9862
}

packages/veui/src/components/ColorPicker/ColorSwatch.vue

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,8 @@
1212
v-bind="{
1313
ui,
1414
readonly,
15-
hue: hsva.h,
16-
saturation: hsva.s,
17-
brightness: hsva.v,
18-
alpha: hsva.a,
15+
hsl,
16+
rgb,
1917
showTip: !!uiProps.tip,
2018
switchable,
2119
alphaChannel: alpha,
@@ -26,7 +24,6 @@
2624
</template>
2725

2826
<script>
29-
import tinycolor from 'tinycolor2'
3027
import ValueAlphaGroup from './_ColorValueAlphaGroup'
3128
import ui from '../../mixins/ui'
3229
import ColorHomer from './mixins/_ColorHomer'
@@ -42,15 +39,6 @@ export default {
4239
type: Boolean,
4340
default: false
4441
}
45-
},
46-
computed: {
47-
hsva () {
48-
let colors = tinycolor(this.color).toHsv()
49-
return Object.keys(colors).reduce(function (obj, key) {
50-
obj[key] = Math.round(colors[key] * 100) / 100
51-
return obj
52-
}, {})
53-
}
5442
}
5543
}
5644
</script>

packages/veui/src/components/ColorPicker/_ColorAlphaSlider.vue

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@
44
:min="0"
55
:max="1"
66
:step="0.01"
7-
:value="value"
8-
@input="updateAlphaValue"
7+
:value="hsl.a"
8+
@input="handleValueUpdate"
99
>
1010
<div
1111
slot="track"
1212
class="veui-slider-custom-track"
1313
:style="{
1414
background: `linear-gradient(to right,
15-
hsla(${hue}, ${saturation * 100}%,${brightness * 100}%, 0),
16-
hsla(${hue}, ${saturation * 100}%,${brightness * 100}%, 1))`
15+
hsla(${hsl.h}, ${hsl.s * 100}%,${hsl.l * 100}%, 0),
16+
hsla(${hsl.h}, ${hsl.s * 100}%,${hsl.l * 100}%, 1))`
1717
}"
1818
/>
1919
<div
@@ -35,10 +35,12 @@ export default {
3535
mixins: [
3636
ColorSlider
3737
],
38-
props: {
39-
hue: Number,
40-
saturation: Number,
41-
brightness: Number
38+
methods: {
39+
handleValueUpdate (val) {
40+
this.updateColor({
41+
a: val
42+
})
43+
}
4244
}
4345
}
4446
</script>

packages/veui/src/components/ColorPicker/_ColorHueSlider.vue

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
:min="0"
55
:max="360"
66
:step="1"
7-
:value="value"
7+
:value="localHue"
88
@input="handleValueUpdate"
99
>
1010
<div
@@ -30,10 +30,28 @@ export default {
3030
mixins: [
3131
ColorSlider
3232
],
33+
data () {
34+
return {
35+
localHue: 0
36+
}
37+
},
38+
watch: {
39+
hsl: {
40+
handler ({h}) {
41+
// Hue 到了 360 时取余归零,为了避免滑块跳变,这里处理一下
42+
if (h || this.localHue % 360) {
43+
this.localHue = h
44+
}
45+
},
46+
deep: true,
47+
immediate: true
48+
}
49+
},
3350
methods: {
3451
handleValueUpdate (val) {
35-
this.updateHsvValue({
36-
h: val
52+
this.localHue = val
53+
this.updateColor({
54+
h: val % 360
3755
})
3856
}
3957
}

packages/veui/src/components/ColorPicker/_ColorShadeField.vue

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -71,39 +71,43 @@
7171
class="veui-color-shade-field-aperture"
7272
:style="{
7373
'background-color': color,
74-
transform: `translate(${aperturePosition.x - 6}px, ${aperturePosition.y - 6}px)`
74+
transform: `translate(${dragCurrentX - 6}px, ${dragCurrentY - 6}px)`
7575
}"
7676
@click.stop
7777
/>
7878
</div>
7979
</template>
8080

8181
<script>
82-
import tinycolor from 'tinycolor2'
82+
// import tinycolor from 'tinycolor2'
8383
import {drag} from '../../directives'
8484
import {clamp} from 'lodash'
8585
import ColorUpdater from './mixins/_ColorUpdater'
86+
import {getTypedAncestorTracker} from '../../utils/helper'
8687
8788
export default {
8889
name: 'color-shade-field',
8990
directives: {
9091
drag
9192
},
9293
mixins: [
93-
ColorUpdater
94+
ColorUpdater,
95+
getTypedAncestorTracker('color-homer')
9496
],
9597
props: {
9698
width: Number,
9799
height: Number,
98-
hue: Number,
99-
saturation: Number,
100-
brightness: Number
100+
color: String,
101+
hsv: Object
101102
},
102103
data () {
103104
return {
104105
isDragging: false,
106+
105107
dragInitX: 0,
106-
dragInitY: 0
108+
dragInitY: 0,
109+
dragCurrentX: 0,
110+
dragCurrentY: 0
107111
}
108112
},
109113
computed: {
@@ -112,41 +116,50 @@ export default {
112116
return Math.round(Math.random() * 0xFFFFFF).toString(36)
113117
},
114118
aperturePosition () {
115-
let saturation = this.saturation
116-
let brightness = this.brightness
119+
let {s, v} = this.hsv
117120
return {
118-
x: saturation * this.width,
119-
y: (1 - brightness) * this.height
121+
x: s * this.width,
122+
y: (1 - v) * this.height
120123
}
121124
},
122-
color () {
123-
return tinycolor({
124-
h: this.hue,
125-
s: this.saturation,
126-
v: this.brightness
127-
}).toHslString()
128-
},
129125
hueColor () {
130-
return tinycolor({
131-
h: this.hue,
132-
s: 1,
133-
v: 1
134-
}).toHexString()
126+
return `hsl(${this.hsv.h}, 100%, 50%)`
127+
}
128+
},
129+
watch: {
130+
aperturePosition: {
131+
handler ({x, y}) {
132+
if (!this.isDragging) {
133+
this.dragCurrentX = x
134+
this.dragCurrentY = y
135+
}
136+
},
137+
immediate: true
138+
},
139+
isDragging (val) {
140+
// 如果拖到底下黑色那块儿,颜色出去转一圈回来 hue 变 0 了,呵呵,锁一下
141+
this.colorHomer.lockHue(val ? this.hsv.h : null)
135142
}
136143
},
137144
mounted () {
138145
this.$on('dragstart', () => {
139146
this.isDragging = true
140-
this.dragInitX = this.aperturePosition.x
141-
this.dragInitY = this.aperturePosition.y
147+
// 一开始没拖的时候还是要从颜色反推位置
148+
this.dragInitX = this.dragCurrentX === undefined ? this.aperturePosition.x : this.dragCurrentX
149+
this.dragInitY = this.dragCurrentY === undefined ? this.aperturePosition.y : this.dragCurrentY
142150
})
143151
this.$on('dragend', () => {
144152
this.isDragging = false
145153
})
146154
this.$on('drag', ({distanceX, distanceY}) => {
147155
let x = this.dragInitX + distanceX
148156
let y = this.dragInitY + distanceY
149-
// 得合在一起传出去(satlig=saturation+brightness)。因为要冒泡到 ColorPicker format成字符串再传回来
157+
158+
// 底下黑色那一块儿颜色都糊在一起,从颜色算出来的坐标就很飘,还是用自己的位置比较稳
159+
this.dragCurrentX = clamp(x, 0, this.width)
160+
this.dragCurrentY = clamp(y, 0, this.height)
161+
162+
// 得合在一起传出去(satlig=saturation+brightness)。因为要到 ColorPicker format成字符串再传回来
150163
// 如果分开的话,后一个到达 ColorPicker 的时候前一个还没生效,所以使用原来的值,导致前一个无法改变
151164
this.updateSatbri(x / this.width, 1 - y / this.height)
152165
})
@@ -157,12 +170,9 @@ export default {
157170
this.$emit('dragend')
158171
},
159172
updateSatbri (saturation, brightness) {
160-
saturation = clamp(saturation, 0, 1)
161-
brightness = clamp(brightness, 0, 1)
162-
this.updateHsvValue({
163-
s: saturation,
164-
v: brightness
165-
})
173+
let s = clamp(saturation, 0, 1)
174+
let v = clamp(brightness, 0, 1)
175+
this.updateColor({s, v})
166176
}
167177
}
168178
}

packages/veui/src/components/ColorPicker/_ColorValueAlpha.vue

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
:format="formatPercentage"
88
:parse="parsePercentage"
99
nudge="percentage"
10-
@input="updateAlphaValue"
10+
@input="handleValueInput"
1111
/>
1212
</div>
1313
</div>
@@ -19,8 +19,15 @@ import ColorValueInput from './mixins/_ColorValueInput'
1919
export default {
2020
name: 'color-value-alpha',
2121
mixins: [ColorValueInput],
22-
props: {
23-
alpha: Number
22+
computed: {
23+
alpha () {
24+
return this.hsl.a === undefined ? 1 : this.hsl.a
25+
}
26+
},
27+
methods: {
28+
handleValueInput (val) {
29+
this.updateColor({a: val})
30+
}
2431
}
2532
}
2633
</script>

0 commit comments

Comments
 (0)