ColorPicker: display current color in real time (#2941)

pull/2961/head
杨奕 2017-02-22 15:14:54 +08:00 committed by baiyaaaaa
parent 1b8657ce0d
commit f2fc8b5fb6
10 changed files with 165 additions and 139 deletions

View File

@ -2,63 +2,42 @@
export default { export default {
data() { data() {
return { return {
color1: '#ff0', color1: '#20a0ff',
color2: null, color2: null,
color3: 'rgba(128, 33, 22, 0.8)', color3: 'rgba(19, 206, 102, 0.8)'
color4: null
}; };
} },
mounted() {
this.$nextTick(() => {
const demos = document.querySelectorAll('.source');
demos[0].style.padding = '0';
});
},
} }
</script> </script>
<style scoped>
.demo-box.demo-color-picker .source {
padding: 0;
}
.demo-box.demo-color-picker .block {
padding: 30px 24px;
overflow: hidden;
border-bottom: solid 1px #EFF2F6;
&:last-child {
border-bottom: none;
}
}
.demo-box.demo-color-picker .demonstration {
display: inline-block;
font-size: 14px;
width: 25%;
color: #8492a6;
line-height: 44px;
}
</style>
## ColorPicker ## ColorPicker
ColorPicker is a color picker component that is used to solve the need to select a color in certain scenes. ColorPicker is a color selector supporting multiple color formats.
### Color ### Basic usage
:::demo ColorPicker usage is similar to DatePicker and requires v-model to bind a variable in a Vue instance. The bind variable's data type needs to be a string. :::demo ColorPicker requires a string typed variable to be bound to v-model.
```html ```html
<div class="block"> <div class="block">
<span class="demonstration">Default value</span> <span class="demonstration">With default value</span>
<el-color-picker v-model="color1"></el-color-picker> <el-color-picker v-model="color1"></el-color-picker>
</div> </div>
<div class="block"> <div class="block">
<span class="demonstration">Empty</span> <span class="demonstration">With no default value</span>
<el-color-picker v-model="color2"></el-color-picker> <el-color-picker v-model="color2"></el-color-picker>
</div> </div>
<style>
</style>
<script> <script>
export default { export default {
data() { data() {
return { return {
color1: '#ff0', color1: '#20a0ff',
color2: null color2: null
} }
} }
@ -67,25 +46,17 @@ ColorPicker is a color picker component that is used to solve the need to select
``` ```
::: :::
### Color and alpha ### Alpha
:::demo ColorPicker supports normal colors, also supports alpha-channel colors, through the show-alpha attribute to control whether to support the use of transparency. :::demo ColorPicker supports alpha channel selecting. To activate alpha selecting, just add the `show-alpha` attribute.
```html ```html
<div class="block"> <el-color-picker v-model="color3" show-alpha></el-color-picker>
<span class="demonstration">Default value</span>
<el-color-picker v-model="color3" show-alpha></el-color-picker>
</div>
<div class="block">
<span class="demonstration">Empty</span>
<el-color-picker v-model="color4" show-alpha></el-color-picker>
</div>
<script> <script>
export default { export default {
data() { data() {
return { return {
color3: 'rgba(128, 33, 22, 0.8)', color3: 'rgba(19, 206, 102, 0.8)'
color4: null
} }
} }
}; };
@ -96,5 +67,5 @@ ColorPicker is a color picker component that is used to solve the need to select
### Attributes ### Attributes
| Attribute | Description | Type | Accepted Values | Default | | Attribute | Description | Type | Accepted Values | Default |
|---------- |-------- |---------- |------------- |-------- | |---------- |-------- |---------- |------------- |-------- |
| show-alpha | Whether to display the alpha slider. | Boolean | — | false | | show-alpha | whether to display the alpha slider | boolean | — | false |
| color-format | Write the v-model's color format. In the case of show-alpha is true, the default value is rgb, otherwise hex. | string | hsl, hsv, hex, rgb | hex | | color-format | color format of v-model | string | hsl / hsv / hex / rgb | hex (when show-alpha is false)/ rgb (when show-alpha is true) |

View File

@ -2,45 +2,47 @@
export default { export default {
data() { data() {
return { return {
color1: '#ff0', color1: '#20a0ff',
color2: null, color2: null,
color3: 'rgba(128, 33, 22, 0.8)', color3: 'rgba(19, 206, 102, 0.8)'
color4: null
}; };
} },
mounted() {
this.$nextTick(() => {
const demos = document.querySelectorAll('.source');
demos[0].style.padding = '0';
});
},
} }
</script> </script>
<style scoped> <style>
.demo-box.demo-color-picker .source { .demo-color-picker .block {
padding: 0; padding: 30px 0;
} text-align: center;
border-right: solid 1px #EFF2F6;
.demo-box.demo-color-picker .block { float: left;
padding: 30px 24px; width: 50%;
overflow: hidden; box-sizing: border-box;
border-bottom: solid 1px #EFF2F6;
&:last-child { &:last-child {
border-bottom: none; border-right: none;
} }
} }
.demo-color-picker .demonstration {
.demo-box.demo-color-picker .demonstration { display: block;
display: inline-block;
font-size: 14px;
width: 25%;
color: #8492a6; color: #8492a6;
line-height: 44px; font-size: 14px;
margin-bottom: 20px;
} }
</style> </style>
## ColorPicker ## ColorPicker 颜色选择器
ColorPicker 是一个颜色选择器,该组件是用来解决某些场景下需要选择颜色的需求 用于颜色选择,支持多种格式
### 选择颜色 ### 基础用法
:::demo ColorPicker 用法与 DatePicker 类似,需要使用 v-model 与 Vue 实例中的一个变量进行双向绑定,绑定的变量需要是字符串类型。 :::demo 使用 v-model 与 Vue 实例中的一个变量进行双向绑定,绑定的变量需要是字符串类型。
```html ```html
<div class="block"> <div class="block">
<span class="demonstration">有默认值</span> <span class="demonstration">有默认值</span>
@ -51,14 +53,11 @@ ColorPicker 是一个颜色选择器,该组件是用来解决某些场景下
<el-color-picker v-model="color2"></el-color-picker> <el-color-picker v-model="color2"></el-color-picker>
</div> </div>
<style>
</style>
<script> <script>
export default { export default {
data() { data() {
return { return {
color1: '#ff0', color1: '#20a0ff',
color2: null color2: null
} }
} }
@ -67,25 +66,17 @@ ColorPicker 是一个颜色选择器,该组件是用来解决某些场景下
``` ```
::: :::
### 选择颜色和透明度 ### 选择透明度
:::demo ColorPicker 支持普通颜色,也支持带 Alpha 通道的颜色,通过 show-alpha 属性即可控制是否支持透明度的使用 :::demo ColorPicker 支持普通颜色,也支持带 Alpha 通道的颜色,通过`show-alpha`属性即可控制是否支持透明度的选择
```html ```html
<div class="block"> <el-color-picker v-model="color3" show-alpha></el-color-picker>
<span class="demonstration">有默认值</span>
<el-color-picker v-model="color3" show-alpha></el-color-picker>
</div>
<div class="block">
<span class="demonstration">无默认值</span>
<el-color-picker v-model="color4" show-alpha></el-color-picker>
</div>
<script> <script>
export default { export default {
data() { data() {
return { return {
color3: 'rgba(128, 33, 22, 0.8)', color3: 'rgba(19, 206, 102, 0.8)'
color4: null
} }
} }
}; };
@ -96,5 +87,5 @@ ColorPicker 是一个颜色选择器,该组件是用来解决某些场景下
### Attributes ### Attributes
| 参数 | 说明 | 类型 | 可选值 | 默认值 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---------- |-------- |---------- |------------- |-------- | |---------- |-------- |---------- |------------- |-------- |
| show-alpha | 是否显示透明度 Slider。 | Boolean | — | false | | show-alpha | 是否支持透明度选择 | boolean | — | false |
| color-format | 写入 v-model 的颜色的格式。在 show-alpha 为 true 的情况下,默认值为 rgb否则为 hex。 | string | hsl, hsv, hex, rgb | hex | | color-format | 写入 v-model 的颜色的格式 | string | hsl / hsv / hex / rgb | hexshow-alpha 为 false/ rgbshow-alpha 为 true |

View File

@ -108,6 +108,10 @@
"path": "/rate", "path": "/rate",
"title": "Rate 评分" "title": "Rate 评分"
}, },
{
"path": "/color-picker",
"title": "ColorPicker 颜色选择器"
},
{ {
"path": "/form", "path": "/form",
"title": "Form 表单" "title": "Form 表单"
@ -219,10 +223,6 @@
{ {
"path": "/collapse", "path": "/collapse",
"title": "Collapse 折叠面板" "title": "Collapse 折叠面板"
},
{
"path": "/color-picker",
"title": "ColorPicker"
} }
] ]
} }
@ -338,6 +338,10 @@
"path": "/rate", "path": "/rate",
"title": "Rate" "title": "Rate"
}, },
{
"path": "/color-picker",
"title": "ColorPicker"
},
{ {
"path": "/form", "path": "/form",
"title": "Form" "title": "Form"
@ -449,10 +453,6 @@
{ {
"path": "/collapse", "path": "/collapse",
"title": "Collapse" "title": "Collapse"
},
{
"path": "/color-picker",
"title": "ColorPicker"
} }
] ]
} }

View File

@ -63,14 +63,24 @@ const parseHexChannel = function(hex) {
}; };
const hsl2hsv = function(hue, sat, light) { const hsl2hsv = function(hue, sat, light) {
sat *= light < 0.5 ? light : 1 - light; sat = sat / 100;
light = light / 100;
let smin = sat;
const lmin = Math.max(light, 0.01);
let sv;
let v;
return [ // [hue, saturation, value] light *= 2;
// Range should be between 0 - 1 sat *= (light <= 1) ? light : 2 - light;
hue, // Hue stays the same smin *= lmin <= 1 ? lmin : 2 - lmin;
2 * sat / (light + sat), // Saturation v = (light + sat) / 2;
light + sat // Value sv = light === 0 ? (2 * smin) / (lmin + smin) : (2 * sat) / (light + sat);
];
return {
h: hue,
s: sv * 100,
v: v * 100
};
}; };
// `rgbToHsv` // `rgbToHsv`
@ -201,38 +211,36 @@ export default class Color {
if (value.indexOf('hsl') !== -1) { if (value.indexOf('hsl') !== -1) {
const parts = value.replace(/hsla|hsl|\(|\)/gm, '') const parts = value.replace(/hsla|hsl|\(|\)/gm, '')
.split(/\s|,/g, '').filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10)); .split(/\s|,/g).filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10));
if (parts.length > 3) { if (parts.length === 4) {
this._alpha = Math.floor(parseFloat(parts[3]) * 100);
}
if (parts.length >= 3) {
const { h, s, v } = hsl2hsv(parts[0], parts[1], parts[2]); const { h, s, v } = hsl2hsv(parts[0], parts[1], parts[2]);
fromHSV(h, s, v); fromHSV(h, s, v);
} }
if (parts.length === 4) {
this._alpha = Math.floor(parseFloat(parts[3]) * 100);
}
} else if (value.indexOf('hsv') !== -1) { } else if (value.indexOf('hsv') !== -1) {
const parts = value.replace(/hsva|hsv|\(|\)/gm, '') const parts = value.replace(/hsva|hsv|\(|\)/gm, '')
.split(/\s|,/g, '').filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10)); .split(/\s|,/g).filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10));
if (parts.length >= 3) {
fromHSV(parts[0], parts[1], parts[2]);
}
if (parts.length === 4) { if (parts.length === 4) {
this._alpha = Math.floor(parseFloat(parts[3]) * 100); this._alpha = Math.floor(parseFloat(parts[3]) * 100);
} }
if (parts.length >= 3) {
fromHSV(parts[0], parts[1], parts[2]);
}
} else if (value.indexOf('rgb') !== -1) { } else if (value.indexOf('rgb') !== -1) {
const parts = value.replace(/rgba|rgb|\(|\)/gm, '') const parts = value.replace(/rgba|rgb|\(|\)/gm, '')
.split(/\s|,/g).filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10)); .split(/\s|,/g).filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10));
if (parts.length === 4) {
this._alpha = Math.floor(parseFloat(parts[3]) * 100);
}
if (parts.length >= 3) { if (parts.length >= 3) {
const { h, s, v } = rgb2hsv(parts[0], parts[1], parts[2]); const { h, s, v } = rgb2hsv(parts[0], parts[1], parts[2]);
fromHSV(h, s, v); fromHSV(h, s, v);
} }
if (parts.length === 4) {
this._alpha = Math.floor(parseFloat(parts[3]) * 100);
}
} else if (value.indexOf('#') !== -1) { } else if (value.indexOf('#') !== -1) {
const hex = value.replace('#', '').trim(); const hex = value.replace('#', '').trim();
let r, g, b; let r, g, b;
@ -259,10 +267,10 @@ export default class Color {
switch (format) { switch (format) {
case 'hsl': case 'hsl':
const hsl = hsv2hsl(_hue, _saturation / 100, _value / 100); const hsl = hsv2hsl(_hue, _saturation / 100, _value / 100);
this.value = `hsla(${ _hue }, ${ hsl[1] * 100 }%, ${ hsl[2] * 100 }%, ${ _alpha / 100})`; this.value = `hsla(${ _hue }, ${ Math.round(hsl[1] * 100) }%, ${ Math.round(hsl[2] * 100) }%, ${ _alpha / 100})`;
break; break;
case 'hsv': case 'hsv':
this.value = `hsva(${ _hue }, ${ _saturation }%, ${ _value }%, ${ _alpha / 100})`; this.value = `hsva(${ _hue }, ${ Math.round(_saturation) }%, ${ Math.round(_value) }%, ${ _alpha / 100})`;
break; break;
default: default:
const { r, g, b } = hsv2rgb(_hue, _saturation, _value); const { r, g, b } = hsv2rgb(_hue, _saturation, _value);
@ -272,10 +280,10 @@ export default class Color {
switch (format) { switch (format) {
case 'hsl': case 'hsl':
const hsl = hsv2hsl(_hue, _saturation / 100, _value / 100); const hsl = hsv2hsl(_hue, _saturation / 100, _value / 100);
this.value = `hsl(${ _hue }, ${ hsl[1] * 100 }%, ${ hsl[2] * 100 }%)`; this.value = `hsl(${ _hue }, ${ Math.round(hsl[1] * 100) }%, ${ Math.round(hsl[2] * 100) }%)`;
break; break;
case 'hsv': case 'hsv':
this.value = `hsv(${ _hue }, ${ _saturation }%, ${ _value }%)`; this.value = `hsv(${ _hue }, ${ Math.round(_saturation) }%, ${ Math.round(_value) }%)`;
break; break;
case 'rgb': case 'rgb':
const { r, g, b } = hsv2rgb(_hue, _saturation, _value); const { r, g, b } = hsv2rgb(_hue, _saturation, _value);

View File

@ -62,13 +62,13 @@
left = Math.max(thumb.offsetWidth / 2, left); left = Math.max(thumb.offsetWidth / 2, left);
left = Math.min(left, rect.width - thumb.offsetWidth / 2); left = Math.min(left, rect.width - thumb.offsetWidth / 2);
this.color._alpha = Math.round((left - thumb.offsetWidth / 2) / (rect.width - thumb.offsetWidth) * 100); this.color.set('alpha', Math.round((left - thumb.offsetWidth / 2) / (rect.width - thumb.offsetWidth) * 100));
} else { } else {
let top = event.clientY - rect.top; let top = event.clientY - rect.top;
top = Math.max(thumb.offsetHeight / 2, top); top = Math.max(thumb.offsetHeight / 2, top);
top = Math.min(top, rect.height - thumb.offsetHeight / 2); top = Math.min(top, rect.height - thumb.offsetHeight / 2);
this.color._alpha = Math.round((top - thumb.offsetHeight / 2) / (rect.height - thumb.offsetHeight) * 100); this.color.set('alpha', Math.round((top - thumb.offsetHeight / 2) / (rect.height - thumb.offsetHeight) * 100));
} }
}, },

View File

@ -9,6 +9,7 @@
</div> </div>
<alpha-slider v-if="showAlpha" ref="alpha" :color="color"></alpha-slider> <alpha-slider v-if="showAlpha" ref="alpha" :color="color"></alpha-slider>
<div class="el-color-dropdown__btns"> <div class="el-color-dropdown__btns">
<span class="el-color-dropdown__value">{{ currentColor }}</span>
<a href="JavaScript:" class="el-color-dropdown__link-btn" @click="$emit('clear')">{{ t('el.colorpicker.clear') }}</a> <a href="JavaScript:" class="el-color-dropdown__link-btn" @click="$emit('clear')">{{ t('el.colorpicker.clear') }}</a>
<button class="el-color-dropdown__btn" @click="confirmValue">{{ t('el.colorpicker.confirm') }}</button> <button class="el-color-dropdown__btn" @click="confirmValue">{{ t('el.colorpicker.confirm') }}</button>
</div> </div>
@ -17,7 +18,7 @@
</template> </template>
<style> <style>
</style> </style>
<script type="babel"> <script>
import SvPanel from './sv-panel'; import SvPanel from './sv-panel';
import HueSlider from './hue-slider'; import HueSlider from './hue-slider';
import AlphaSlider from './alpha-slider'; import AlphaSlider from './alpha-slider';
@ -42,6 +43,13 @@
showAlpha: Boolean showAlpha: Boolean
}, },
computed: {
currentColor() {
const parent = this.$parent;
return !parent.value && !parent.showPanelColor ? '' : parent.color.value;
}
},
methods: { methods: {
confirmValue() { confirmValue() {
this.$emit('pick'); this.$emit('pick');
@ -65,5 +73,5 @@
} }
} }
} }
} };
</script> </script>

View File

@ -18,7 +18,7 @@
<style> <style>
</style> </style>
<script type="babel"> <script>
import draggable from '../draggable'; import draggable from '../draggable';
export default { export default {
@ -91,7 +91,7 @@
cursorTop: 0, cursorTop: 0,
cursorLeft: 0, cursorLeft: 0,
background: 'hsl(0, 100%, 50%)' background: 'hsl(0, 100%, 50%)'
} };
} }
} };
</script> </script>

View File

@ -1,6 +1,8 @@
import Vue from 'vue';
let isDragging = false; let isDragging = false;
export default function(element, options) { export default function(element, options) {
if (Vue.prototype.$isServer) return;
const moveFn = function(event) { const moveFn = function(event) {
if (options.drag) { if (options.drag) {
options.drag(event); options.drag(event);

View File

@ -4,9 +4,9 @@
<span class="el-color-picker__color" :class="{ 'is-alpha': showAlpha }"> <span class="el-color-picker__color" :class="{ 'is-alpha': showAlpha }">
<span class="el-color-picker__color-inner" <span class="el-color-picker__color-inner"
:style="{ :style="{
backgroundColor: value ? value : 'transparent' backgroundColor: displayedColor
}"></span> }"></span>
<span class="el-color-picker__empty el-icon-close" v-if="!value"></span> <span class="el-color-picker__empty el-icon-close" v-if="!value && !showPanelColor"></span>
</span> </span>
<span class="el-color-picker__icon el-icon-caret-bottom"></span> <span class="el-color-picker__icon el-icon-caret-bottom"></span>
</div> </div>
@ -47,11 +47,30 @@
directives: { Clickoutside }, directives: { Clickoutside },
computed: {
displayedColor() {
if (!this.value && !this.showPanelColor) {
return 'transparent';
} else {
const { r, g, b } = this.color.toRgb();
return this.showAlpha
? `rgba(${ r }, ${ g }, ${ b }, ${ this.color.get('alpha') / 100 })`
: `rgb(${ r }, ${ g }, ${ b })`;
}
}
},
watch: { watch: {
value(val) { value(val) {
if (val && val !== this.color.value) { if (val && val !== this.color.value) {
this.color.fromString(val); this.color.fromString(val);
} }
},
color: {
deep: true,
handler() {
this.showPanelColor = true;
}
} }
}, },
@ -62,10 +81,22 @@
}, },
clearValue() { clearValue() {
this.$emit('input', null); this.$emit('input', null);
this.showPanelColor = false;
this.showPicker = false; this.showPicker = false;
this.resetColor();
}, },
hide() { hide() {
this.showPicker = false; this.showPicker = false;
this.resetColor();
},
resetColor() {
this.$nextTick(_ => {
if (this.value) {
this.color.fromString(this.value);
} else {
this.showPanelColor = false;
}
});
} }
}, },
@ -84,7 +115,8 @@
}); });
return { return {
color, color,
showPicker: false showPicker: false,
showPanelColor: false
}; };
}, },

View File

@ -4,7 +4,7 @@
@component hue-slider { @component hue-slider {
position: relative; position: relative;
box-sizing: border-box; box-sizing: border-box;
width: 240px; width: 280px;
height: 12px; height: 12px;
background-color: #f00; background-color: #f00;
padding: 0 2px; padding: 0 2px;
@ -58,7 +58,7 @@
@component svpanel { @component svpanel {
position: relative; position: relative;
width: 240px; width: 280px;
height: 180px; height: 180px;
@descendent white, black { @descendent white, black {
@ -94,7 +94,7 @@
@component alpha-slider { @component alpha-slider {
position: relative; position: relative;
box-sizing: border-box; box-sizing: border-box;
width: 240px; width: 280px;
height: 12px; height: 12px;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==); background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);
@ -141,7 +141,7 @@
} }
@component dropdown { @component dropdown {
width: 260px; width: 300px;
@descendent main-wrapper { @descendent main-wrapper {
margin-bottom: 6px; margin-bottom: 6px;
@ -158,6 +158,13 @@
text-align: right; text-align: right;
} }
@descendent value {
float: left;
line-height: 26px;
font-size: 12px;
color: var(--color-base-black);
}
@descendent btn { @descendent btn {
border: 1px solid #dcdcdc; border: 1px solid #dcdcdc;
color: #333; color: #333;
@ -173,6 +180,10 @@
color: #cccccc; color: #cccccc;
cursor: not-allowed; cursor: not-allowed;
} }
&:hover {
color: var(--color-primary);
border-color: var(--color-primary);
}
} }
@descendent link-btn { @descendent link-btn {
@ -181,6 +192,9 @@
text-decoration: none; text-decoration: none;
padding: 15px; padding: 15px;
font-size: 12px; font-size: 12px;
&:hover {
color: tint(var(--color-primary), var(--button-hover-tint-percent));
}
} }
} }