1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
| <template>
| <div
| ref="svPanel"
| class="ant-color-svpanel"
| :style="{
| backgroundColor: background,
| }"
| >
| <div class="ant-color-svpanel__white"></div>
| <div class="ant-color-svpanel__black"></div>
| <div
| class="ant-color-svpanel__cursor"
| :style="{
| top: cursorTop + 'px',
| left: cursorLeft + 'px',
| }"
| ></div>
| </div>
| </template>
|
| <script>
| import draggable from '../lib/draggable';
|
| export default {
| name: 'SvPanel-1',
| props: {
| color: {
| type: Object,
| },
| },
| data() {
| return {
| panelWidth: 0,
| panelHeight: 0,
| cursorTop: 0,
| cursorLeft: 0,
| background: 'hsl(0, 100%, 50%)',
| };
| },
| computed: {
| colorValue() {
| const saturation = this.color?.get('saturation');
| const hue = this.color?.get('hue') || 0;
| const value = this.color?.get('value');
| return {saturation, hue, value};
| },
| },
| watch: {
| colorValue: {
| immediate: true,
| handler() {
| this.$nextTick(() => {
| this.update();
| });
| },
| },
| },
| methods: {
| update() {
| this.panelWidth = this.$refs['svPanel'].clientWidth;
| this.panelHeight = this.$refs['svPanel'].clientHeight;
| const {saturation, hue, value} = this.colorValue;
| this.cursorLeft = (saturation * this.panelWidth) / 100;
| this.cursorTop = ((100 - value) * this.panelHeight) / 100;
| this.background = `hsl(${hue}, 100%, 50%)`;
| },
| handleDrag(event) {
| const rect = this.$el.getBoundingClientRect();
|
|
| let left = event.clientX - rect.left;
| let top = event.clientY - rect.top;
|
| left = Math.max(0, left);
| left = Math.min(left, rect.width);
| top = Math.max(0, top);
| top = Math.min(top, rect.height);
|
| this.cursorLeft = left;
| this.cursorTop = top;
|
| this.color.set({
| saturation: (left / rect.width) * 100,
| value: 100 - (top / rect.height) * 100,
| });
| },
| },
|
| mounted() {
| draggable(this.$el, {
| drag: (event) => {
| this.handleDrag(event);
| },
| end: (event) => {
| this.handleDrag(event);
| },
| });
| },
| };
| </script>
|
| <style scoped>
| .ant-color-svpanel {
| position: relative;
| height: 180px;
| border-radius: 3px;
| overflow: hidden;
| }
|
| .ant-color-svpanel__white,
| .ant-color-svpanel__black {
| position: absolute;
| top: 0;
| left: 0;
| right: 0;
| bottom: 0;
| }
|
| .ant-color-svpanel__white {
| background: linear-gradient(to right, #fff, rgba(255, 255, 255, 0));
| }
|
| .ant-color-svpanel__black {
| background: linear-gradient(to top, #000, rgba(0, 0, 0, 0));
| }
|
| .ant-color-svpanel__cursor {
| position: absolute;
| width: 18px;
| height: 18px;
| box-shadow: 0 0 0 1.5px #fff, inset 0 0 1px 1px rgba(0, 0, 0, 0.3),
| 0 0 1px 2px rgba(0, 0, 0, 0.4);
| border-radius: 50%;
| transform: translate(-50%, -50%);
| }
| </style>
|
|