This repository has been archived on 2023-08-30. You can view files and clone it, but cannot push or open issues/pull-requests.
bytedo
/
wcui
Archived
1
0
Fork 0
wcui/src/color/index.wc

542 lines
13 KiB
Plaintext

<template>
<div class="color-picker">
<section class="preview alpha-bg">
<span tabindex="0"></span>
</section>
<div class="color-panel">
<section class="dashboard">
<div class="scene"><span class="thumb"></span></div>
<div class="pool">
<span class="thumb hue-thumb"></span>
<input class="hue" max="360" min="0" step="1" type="range" />
</div>
</section>
<section class="alpha-box alpha-bg">
<div class="bar"></div>
<span class="thumb alpha-thumb"></span>
<input class="alpha" max="100" min="0" step="1" type="range" />
</section>
<section class="input-box">
<input class="input" maxlength="28" />
<a class="clear">清除</a>
<a tabindex="0" class="submit">确定</a>
</section>
</div>
</div>
</template>
<style lang="scss">
:host {
display: inline-flex;
}
.alpha-bg {
background: linear-gradient(
45deg,
var(--color-grey-1) 25%,
transparent 25%,
transparent 75%,
var(--color-grey-1) 75%,
var(--color-grey-1)
),
linear-gradient(
45deg,
var(--color-grey-1) 25%,
transparent 25%,
transparent 75%,
var(--color-grey-1) 75%,
var(--color-grey-1)
);
background-size: 12px 12px;
background-position: 0 0, 6px 6px;
}
.color-picker {
position: relative;
width: 36px;
height: 36px;
.preview {
display: flex;
width: 100%;
height: 100%;
border: 2px solid var(--color-plain-2);
border-radius: 6px;
cursor: pointer;
transition: box-shadow 0.15s linear;
span {
width: 100%;
height: 100%;
border: 3px solid #fff;
border-radius: 6px;
outline: none;
}
&:focus-within {
box-shadow: 0 0 0 2px var(--color-plain-a);
}
}
.color-panel {
display: none;
position: absolute;
left: 0;
top: 38px;
width: 310px;
padding: 5px;
background: #fff;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
.dashboard {
display: flex;
justify-content: space-between;
.scene {
overflow: hidden;
position: relative;
width: 280px;
height: 180px;
background: #f00;
&::before,
&::after {
position: absolute;
left: 0;
top: 0;
z-index: 0;
width: 100%;
height: 100%;
content: '';
}
&::before {
background: linear-gradient(to right, #fff, transparent);
}
&::after {
background: linear-gradient(to bottom, transparent, #000);
}
.thumb {
position: absolute;
z-index: 99;
width: 0;
height: 0;
&::after {
display: block;
width: 10px;
height: 10px;
border-radius: 50%;
background: rgba(32, 32, 32, 0.3);
box-shadow: 0 0 0 1px #fff;
transform: translate(-5px, -5px);
content: '';
}
}
}
.pool {
overflow: hidden;
position: relative;
width: 12px;
height: 180px;
background: linear-gradient(
to bottom,
#f00 0,
#ff0 17%,
#0f0 33%,
#0ff 50%,
#00f 67%,
#f0f 83%,
#f00
);
.thumb {
position: absolute;
left: 0;
top: 0;
z-index: 0;
width: 12px;
height: 0;
&::after {
display: block;
width: 12px;
height: 12px;
border-radius: 50%;
background: #fff;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
transform: translateY(-6px);
content: '';
}
}
.hue {
position: relative;
z-index: 1;
display: block;
width: 12px;
height: 180px;
appearance: slider-vertical;
opacity: 0;
}
}
}
.alpha-box {
overflow: hidden;
position: relative;
width: 100%;
height: 12px;
margin: 12px 0;
.bar {
position: absolute;
left: 0;
top: 0;
z-index: 0;
width: 100%;
height: 12px;
background: linear-gradient(to right, transparent, #f00);
}
.thumb {
position: absolute;
left: 100%;
top: 0;
z-index: 0;
width: 0;
height: 12px;
&::after {
display: block;
width: 12px;
height: 12px;
border-radius: 50%;
background: #fff;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
transform: translateX(-6px);
content: '';
}
}
.alpha {
position: relative;
z-index: 9;
display: block;
width: 100%;
height: 12px;
opacity: 0;
}
}
.input-box {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 0;
.input {
width: 200px;
height: 24px;
padding: 0 6px;
line-height: 22px;
font: inherit;
font-size: 12px;
border: 2px solid var(--color-plain-2);
border-radius: 4px;
outline: none;
color: var(--color-dark-1);
transition: box-shadow 0.15s linear;
&::placeholder {
color: var(--color-grey-1);
}
&:focus {
box-shadow: 0 0 0 2px var(--color-plain-a);
}
}
.clear,
.submit {
font-size: 12px;
cursor: pointer;
user-select: none;
}
.clear {
color: var(--color-grey-3);
}
.submit {
padding: 2px 6px;
border-radius: 2px;
color: var(--color-plain-1);
background: var(--color-teal-2);
outline: none;
transition: box-shadow 0.15s linear, background 0.15s linear;
&:hover {
background: var(--color-teal-1);
}
&:focus {
box-shadow: 0 0 0 2px var(--color-teal-a);
}
}
}
}
}
</style>
<script>
import $ from '../utils'
import { hsb2rgb, rgb2hex, hex2rgb, rgb2hsb } from './helper'
export default class Color {
props = {
value: ''
}
state = {
hsb: { h: 0, s: 100, b: 100 },
rgba: { r: 255, g: 0, b: 0, a: 100 }
}
__init__() {
/* render */
var elem = this.root.children[1]
this.__PREVIEW__ = elem.children[0].children[0]
this.__PANEL__ = elem.children[1]
this.__SCENE__ = elem.querySelector('.scene')
this.__SCENE_THUMB__ = this.__SCENE__.querySelector('.thumb')
this.__HUE__ = elem.querySelector('.hue')
this.__HUE_THUMB__ = elem.querySelector('.hue-thumb')
this.__ALPHA__ = elem.querySelector('.alpha')
this.__ALPHA_BAR__ = elem.querySelector('.alpha-box .bar')
this.__ALPHA_THUMB__ = elem.querySelector('.alpha-thumb')
this.__INPUT__ = elem.querySelector('.input')
this.__CLEAR__ = elem.querySelector('.clear')
this.__PICKED__ = elem.querySelector('.submit')
}
_updateView() {
var { hsb, rgba } = this.state
var scene, color, alphaColor
var x, y
x = Math.ceil((hsb.s * 280) / 100)
y = 180 - Math.ceil((hsb.b * 180) / 100)
scene = '#' + rgb2hex(hsb2rgb({ h: hsb.h, s: 100, b: 100 }))
alphaColor = '#' + rgb2hex(rgba)
if (rgba.a < 100) {
color = `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a / 100})`
} else {
color = alphaColor
}
this._moveSceneThumb(x, y)
this.__HUE_THUMB__.style.top = hsb.h / 2 + 'px'
this.__SCENE__.style.backgroundColor = scene
this.__PREVIEW__.style.backgroundColor = color
this.__ALPHA_BAR__.style.background = `linear-gradient(to right, transparent, ${alphaColor})`
this.__ALPHA__.value = rgba.a
this.__ALPHA_THUMB__.style.left = rgba.a + '%'
this.__INPUT__.value = color
this.props.value = color
}
_moveSceneThumb(x, y) {
this.__SCENE_THUMB__.style.cssText = `left: ${x}px; top:${y}px`
}
_changeColor(x, y) {
var { rgba, hsb } = this.state
var alphaColor
hsb.s = ~~((100 * x) / 280)
hsb.b = ~~((100 * (180 - y)) / 180)
Object.assign(rgba, hsb2rgb(hsb))
var { r, g, b, a } = rgba
if (rgba.a < 100) {
this.__INPUT__.value = `rgba(${r}, ${g}, ${b}, ${a / 100})`
} else {
this.__INPUT__.value = `#${rgb2hex({ r, g, b })}`
}
alphaColor = `linear-gradient(to right, transparent, rgb(${r}, ${g}, ${b}))`
this.__PREVIEW__.style.backgroundColor = this.__INPUT__.value
this.__ALPHA_BAR__.style.background = alphaColor
this._moveSceneThumb(x, y)
this.dispatchEvent(new CustomEvent('input'))
}
get value() {
return this.props.value
}
set value(val) {
var isHex
var rgb
val = val.toLowerCase()
if (!val) {
return
}
isHex = /^#[0-9a-f]{3,6}$/.test(val)
if (isHex) {
Object.assign(this.state.rgba, hex2rgb(val), { a: 100 })
} else {
var res = val.match(/rgba?\((\d+),\s*?(\d+),\s*?(\d+)[,\s]*?([\d\.]+)?\)/)
if (res) {
this.state.rgba = { r: +res[1], g: +res[2], b: +res[3], a: 100 }
if (res[4] !== undefined) {
this.state.rgba.a = ~~(res[4] * 100)
}
} else {
return
}
}
this.state.hsb = rgb2hsb(this.state.rgba)
this._updateView()
}
mounted() {
var handleMove
this.handleDown = $.bind(this.__SCENE__, 'mousedown', ev => {
ev.preventDefault()
var { pageX, pageY } = ev
var { left, top } = $.offset(this.__SCENE__)
var w = 280
var h = 180
var x = pageX - left
var y = pageY - top
this._changeColor(x, y)
handleMove = $.bind(document, 'mousemove', ev => {
ev.preventDefault()
var { pageX, pageY } = ev
var x = pageX - left
var y = pageY - top
var rgb
x = x < 0 ? 0 : x > 280 ? 280 : x
y = y < 0 ? 0 : y > 180 ? 180 : y
this._changeColor(x, y)
})
})
this.handleUp = $.bind(document, 'mouseup', ev => {
ev.preventDefault()
$.unbind(document, 'mousemove', handleMove)
})
// 颜色池
this.handleInput1 = $.bind(this.__HUE__, 'input', ev => {
var h = 360 - this.__HUE__.value
var { s, b } = this.state.hsb
var rgba = this.state.rgba
var hsb = { h, s, b }
var scene, color, alphaColor
Object.assign(rgba, hsb2rgb(hsb))
this.state.hsb = hsb
this.state.rgba = rgba
scene = '#' + rgb2hex(hsb2rgb({ h, s: 100, b: 100 }))
alphaColor = '#' + rgb2hex(rgba)
if (rgba.a < 100) {
color = `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a / 100})`
} else {
color = alphaColor
}
this.__HUE_THUMB__.style.top = h / 2 + 'px'
this.__SCENE__.style.backgroundColor = scene
this.__PREVIEW__.style.backgroundColor = color
this.__ALPHA_BAR__.style.background = `linear-gradient(to right, transparent, ${alphaColor})`
this.__INPUT__.value = color
})
// 透明度
this.handleInput2 = $.bind(this.__ALPHA__, 'input', ev => {
var a = this.__ALPHA__.value
var { r, g, b } = this.state.rgba
var color = `rgba(${r}, ${g}, ${b}, ${a / 100})`
this.state.rgba.a = a
this.__ALPHA_THUMB__.style.left = a + '%'
this.__PREVIEW__.style.backgroundColor = color
this.__INPUT__.value = color
})
this._clickFn = $.bind(this.__PREVIEW__, 'click', ev => {
this.__PANEL__.style.display = 'block'
this._tmpval = this.props.value
})
this._clearFn = $.bind(this.__CLEAR__, 'click', ev => {
this.__PANEL__.style.display = ''
this.__INPUT__.value = ''
this.__PREVIEW__.style = ''
this.props.value = this.__INPUT__.value
delete this._tmpval
this.dispatchEvent(new CustomEvent('input'))
})
this._pickedFn = $.bind(this.__PICKED__, 'click', ev => {
this.__PANEL__.style.display = ''
this.__PREVIEW__.style.backgroundColor = this.__INPUT__.value
this.props.value = this.__INPUT__.value
delete this._tmpval
this.dispatchEvent(new CustomEvent('input'))
})
// 点击外部区别时,还原之前的颜色值
this._outsideFn = $.outside(this.__PANEL__, ev => {
if (this.hasOwnProperty('_tmpval')) {
this.__PANEL__.style.display = ''
this.value = this._tmpval
delete this._tmpval
}
})
}
unmount() {
$.unbind(this.__SCENE__, 'mousedown', this.handleDown)
$.unbind(document, 'mousedown', this.handleUp)
$.unbind(this.__HUE__, 'input', this.handleInput1)
$.unbind(this.__ALPHA__, 'input', this.handleInput2)
$.unbind(this.__PREVIEW__, 'input', this._clickFn)
$.unbind(this.__CLEAR__, 'input', this._clearFn)
$.unbind(this.__PICKED__, 'input', this._pickedFn)
$.clearOutside(this._outsideFn)
delete this.handleDown
delete this.handleUp
delete this.handleChange
delete this.handleInput1
delete this.handleInput2
}
}
</script>
wcui是一套基于`Web Components`的UI组件库, 宗旨是追求简单、实用、不花哨。
JavaScript 95.2%
CSS 4.8%