颜色取色器

1. 添加滑动选取结束事件
2. 修复多个按钮无法关闭上一个窗口bug
This commit is contained in:
KeiferJu 2020-06-11 09:35:20 +08:00
parent 69eb33632a
commit 6bbebb0160
4 changed files with 218 additions and 206 deletions

View File

@ -2369,6 +2369,11 @@
"description": "选择改变事件",
"name": "change",
"values": ""
},{
"isEvent": true,
"description": "改变完成事件",
"name": "changeEnd",
"values": ""
}
],
"Slider": [

View File

@ -36,6 +36,6 @@
<div class="alpha {className}" class:vertical class:horizontal={!vertical}>
<div class="alpha-in" style="background: linear-gradient(to {toGradient}, transparent 0%, {color} 100%)">
<Slider bind:value={a} {vertical} on:input on:input={(event) => console.log(event.detail)} />
<Slider bind:value={a} {vertical} on:input on:input={(event) => console.log()} />
</div>
</div>

View File

@ -1,25 +1,126 @@
<svelte:options accessors={true} />
<script>
import { createEventDispatcher } from 'svelte'
const dispatch = createEventDispatcher()
// input
import tinycolor from 'tinycolor2'
import { getValidColor } from './utils.js'
import SaturationValue from './SaturationValue.svelte'
import Alpha from './Alpha.svelte'
import Hue from './Hue.svelte'
import ColorSquare from './ColorSquare.svelte'
// RED
export let h = 0
export let s = 1
export let v = 1
export let l = 0.5
export let r = 255
export let g = 0
export let b = 0
export let hex = '#ff0000'
export let a = 1
export let color
$: color = { r, g, b, h, s, l, v, a, hex }
export let startColor = '#ff0000' // all tinycolor colors
export let disableAlpha = false
export let fieldsIndex = 0
export const setColor = args => update(args, false)
const update = (args, dispatch = true) => {
// is not enough with color.isValidColor
const color = getValidColor(args)
if (!color) return
const format = color.getFormat()
// we dont use hex8
;(format === 'hex' || format === 'hex8') && color.setAlpha(a)
const _rgba = color.toRgb()
const _hsla = color.toHsl()
const _hsva = color.toHsv()
const _hex = `#${color.toHex()}`
r = args.r != null ? args.r : _rgba.r
g = args.g != null ? args.g : _rgba.g
b = args.b != null ? args.b : _rgba.b
h = args.h != null ? args.h : _hsla.h
s = args.s != null ? args.s : _hsla.s
l = args.l != null ? args.l : _hsla.l
v = args.v != null ? args.v : _hsva.v
a = args.a != null ? args.a : _rgba.a
hex = format === 'hex' ? args : _hex
dispatch && dispatchInput()
}
const updateAlpha = alpha => {
if (isNaN(alpha) || alpha < 0 || alpha > 1) return
a = alpha
dispatchInput()
}
const dispatchInput = () => {
const value = getcolorValue()
dispatch('input', value)
}
const onlyChars = chars => event =>
chars.indexOf(String.fromCharCode(event.charCode)) === -1 && event.preventDefault()
const onlyNumbers = onlyChars('0123456789')
const onlyNumbersAndDot = onlyChars('0123456789.')
update(startColor, false)
function getcolorValue() {
switch (fieldsIndex) {
case 1:
const rgba = `rgba(${color.r},${color.g},${color.b},${color.a})`
return rgba
break
case 2:
const hsla = `hsla(${Math.round(color.h) % 360},${Math.round(color.s * 100)}%,${Math.round(
color.l * 100
)}%,${Math.round(color.a * 100) / 100})`
return hsla
break
default:
const hex = color.hex
return hex
}
}
function mouseupEvent() {
const value = getcolorValue()
dispatch('mouseupEvent', value)
}
</script>
<style>
.color-picker{
.color-picker {
display: flex;
flex-direction: column;
width: 14.5em;
box-shadow: 0 0 2px rgba(0,0,0,.3), 0 4px 8px rgba(0,0,0,.3);
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3), 0 4px 8px rgba(0, 0, 0, 0.3);
background: #fff;
}
.color-picker :global(.saturation-value){
.color-picker :global(.saturation-value) {
height: 9em; /* 14.5 / 1.618 */
}
.sliders-and-square{
.sliders-and-square {
display: flex;
flex-direction: row;
margin-top: 1em;
}
.square-wrap{
.square-wrap {
width: 2em;
height: 2em;
border-radius: 1.5em;
@ -28,24 +129,24 @@
overflow: hidden;
}
.sliders{
.sliders {
display: flex;
flex-direction: column;
flex: 1;
margin: auto 1em auto 0;
}
.alpha-wrap{
.alpha-wrap {
margin-top: 0.75em;
}
.inputs-and-changer{
.inputs-and-changer {
display: flex;
flex-direction: row;
padding: 1em 0.5em;
}
.changer-wrap{
.changer-wrap {
box-sizing: border-box;
width: 2em;
flex: none;
@ -53,33 +154,34 @@
padding-left: 0.5em;
}
.changer-up, .changer-down{
.changer-up,
.changer-down {
margin: auto;
cursor: pointer;
}
.changer-up {
width: 0;
height: 0;
width: 0;
height: 0;
border-left: 0.5em solid transparent;
border-right: 0.5em solid transparent;
border-bottom: 0.5em solid #666;
}
.changer-down {
width: 0;
height: 0;
width: 0;
height: 0;
border-left: 0.5em solid transparent;
border-right: 0.5em solid transparent;
border-top: 0.5em solid #666;
margin-top: 0.5em;
}
.inputs-wrap{
.inputs-wrap {
flex: 1;
}
input{
input {
text-align: center;
outline: 0;
box-shadow: none;
@ -92,31 +194,32 @@
padding: 0.25em 0;
}
.hex{
.hex {
width: 100%;
margin: auto;
}
.rgba-wrap, .hsla-wrap{
.rgba-wrap,
.hsla-wrap {
display: flex;
}
.rgba-wrap > div:not(:first-child),
.hsla-wrap > div:not(:first-child){
.hsla-wrap > div:not(:first-child) {
margin-left: 0.5em;
}
.rgba-wrap input,
.hsla-wrap input{
.hsla-wrap input {
width: 100%;
}
.percent-input{
.percent-input {
position: relative;
}
.percent-input:after{
content: "%";
.percent-input:after {
content: '%';
display: block;
position: absolute;
top: 50%;
@ -124,7 +227,7 @@
right: 0.25em;
}
label{
label {
display: block;
text-transform: uppercase;
text-align: center;
@ -134,142 +237,44 @@
}
</style>
<script>
import {createEventDispatcher} from "svelte";
const dispatch = createEventDispatcher();
// input
import tinycolor from "tinycolor2";
import {getValidColor} from "./utils.js"
import SaturationValue from "./SaturationValue.svelte";
import Alpha from "./Alpha.svelte"
import Hue from "./Hue.svelte";
import ColorSquare from "./ColorSquare.svelte";
// RED
export let h = 0;
export let s = 1;
export let v = 1;
export let l = 0.5;
export let r = 255;
export let g = 0;
export let b = 0;
export let hex = "#ff0000";
export let a = 1;
export let color;
$: color = {r, g, b, h, s, l, v, a, hex};
export let startColor = "#ff0000"; // all tinycolor colors
export let disableAlpha = false;
export let fieldsIndex = 0;
export const setColor = (args) => update(args, false);
const update = (args, dispatch=true) => {
// is not enough with color.isValidColor
const color = getValidColor(args);
if(!color) return;
const format = color.getFormat();
// we dont use hex8
(format === "hex" || format === "hex8") && color.setAlpha(a);
const _rgba = color.toRgb();
const _hsla = color.toHsl();
const _hsva = color.toHsv();
const _hex = `#${color.toHex()}`;
r = args.r != null ? args.r : _rgba.r;
g = args.g != null ? args.g : _rgba.g;
b = args.b != null ? args.b : _rgba.b;
h = args.h != null ? args.h : _hsla.h;
s = args.s != null ? args.s : _hsla.s;
l = args.l != null ? args.l : _hsla.l;
v = args.v != null ? args.v : _hsva.v;
a = args.a != null ? args.a : _rgba.a;
hex = format === "hex" ? args : _hex;
dispatch && dispatchInput();
}
const updateAlpha = (alpha) => {
if(isNaN(alpha) || alpha < 0 || alpha > 1)
return;
a = alpha;
dispatchInput()
}
const dispatchInput = () =>{
const value = getcolorValue();
dispatch("input", value)
} ;
const onlyChars = (chars) => (event) => chars.indexOf(String.fromCharCode(event.charCode)) === -1 && event.preventDefault();
const onlyNumbers = onlyChars("0123456789");
const onlyNumbersAndDot = onlyChars("0123456789.");
update(startColor, false);
function getcolorValue(){
switch(fieldsIndex){
case 1:
const rgba = `rgba(${color.r},${color.g},${color.b},${color.a})`;
return rgba;
break;
case 2:
const hsla = `hsla(${Math.round(color.h) % 360},${Math.round(color.s * 100)}%,${Math.round(color.l * 100)}%,${Math.round(color.a * 100) / 100})`;
return hsla;
break;
default:
const hex = color.hex;
return hex;
}
}
</script>
<svelte:options accessors={true} />
<div class="color-picker">
<div class="saturation-value-wrap">
<SaturationValue {h} {s} {v} on:input={(event) => update({h, s: event.detail.s, v: event.detail.v, a})} />
<div class="saturation-value-wrap" on:mouseup={mouseupEvent}>
<SaturationValue {h} {s} {v} on:input={event => update({ h, s: event.detail.s, v: event.detail.v, a })} />
</div>
<div class="sliders-and-square">
<div class="square-wrap">
<ColorSquare color="rgba({r}, {g}, {b}, {a})"/>
<ColorSquare color="rgba({r}, {g}, {b}, {a})" />
</div>
<div class="sliders">
<div class="hue-wrap">
<Hue {h} on:input={event => update({h: event.detail, s, v, a})} />
<div class="hue-wrap" on:mouseup={mouseupEvent}>
<Hue {h} on:input={event => update({ h: event.detail, s, v, a })} />
</div>
{#if !disableAlpha}
<div class="alpha-wrap">
<Alpha bind:a color={hex} on:input={dispatchInput}/>
<div class="alpha-wrap" on:mouseup={mouseupEvent}>
<Alpha bind:a color={hex} on:input={dispatchInput} />
</div>
{/if}
</div>
</div>
<div class="inputs-and-changer">
<div class="inputs-wrap">
{#if fieldsIndex === 0}
<div class="input-wrap hex-wrap">
<input
<input
class="hex"
type="text"
value={hex}
maxlength={7}
on:keypress={onlyChars("#0123456789abcdefABCFDEF")}
on:input={event => update(event.target.value)}
/>
on:keypress={onlyChars('#0123456789abcdefABCFDEF')}
on:input={event => update(event.target.value)} />
<label>hex</label>
</div>
{:else if fieldsIndex === 1}
@ -281,8 +286,7 @@
value={r}
maxlength={3}
on:keypress={onlyNumbers}
on:input={event => update({r: parseInt(event.target.value), g, b, a})}
/>
on:input={event => update({ r: parseInt(event.target.value), g, b, a })} />
<label>r</label>
</div>
<div class="input-wrap">
@ -292,18 +296,17 @@
value={g}
maxlength={3}
on:keypress={onlyNumbers}
on:input={event => update({r, g: parseInt(event.target.value), b, a})}
/>
on:input={event => update({ r, g: parseInt(event.target.value), b, a })} />
<label>g</label>
</div>
<div class="input-wrap">
<input class="rgba"
<input
class="rgba"
type="text"
value={b}
maxlength={3}
on:keypress={onlyNumbers}
on:input={event => update({r, g, b: parseInt(event.target.value), a})}
/>
on:input={event => update({ r, g, b: parseInt(event.target.value), a })} />
<label>b</label>
</div>
{#if !disableAlpha}
@ -314,8 +317,7 @@
value={Math.round(a * 100) / 100}
maxlength={4}
on:keypress={onlyNumbersAndDot}
on:input={event => updateAlpha(parseFloat(event.target.value))}
/>
on:input={event => updateAlpha(parseFloat(event.target.value))} />
<label>a</label>
</div>
{/if}
@ -323,13 +325,13 @@
{:else if fieldsIndex === 2}
<div class="hsla-wrap">
<div class="input-wrap">
<input class="hsla"
<input
class="hsla"
value={Math.round(h) % 360}
type="text"
maxlength={3}
on:keypress={onlyNumbers}
on:input={event => update({h: parseInt(event.target.value), s, l, a})}
/>
on:input={event => update({ h: parseInt(event.target.value), s, l, a })} />
<label>h</label>
</div>
<div class="input-wrap">
@ -339,8 +341,7 @@
type="text"
maxlength={4}
on:keypress={onlyNumbers}
on:input={event => update({h, s: parseFloat(event.target.value) / 100, l, a})}
/>
on:input={event => update({ h, s: parseFloat(event.target.value) / 100, l, a })} />
<label>s</label>
</div>
<div class="input-wrap">
@ -350,8 +351,7 @@
type="text"
maxlength={4}
on:keypress={onlyNumbers}
on:input={event => update({h, s, l: parseFloat(event.target.value) / 100, a})}
/>
on:input={event => update({ h, s, l: parseFloat(event.target.value) / 100, a })} />
<label>l</label>
</div>
{#if !disableAlpha}
@ -362,8 +362,7 @@
type="text"
maxlength={4}
on:keypress={onlyNumbersAndDot}
on:input={event => updateAlpha(parseFloat(event.target.value))}
/>
on:input={event => updateAlpha(parseFloat(event.target.value))} />
<label>a</label>
</div>
{/if}
@ -372,9 +371,9 @@
</div>
<div class="changer-wrap">
<div class="changer-up" on:click={() => fieldsIndex = (fieldsIndex === 0 ? 2 : (fieldsIndex - 1) % 3)}></div>
<div class="changer-down" on:click={() => fieldsIndex = (fieldsIndex + 1) % 3}></div>
<div class="changer-up" on:click={() => (fieldsIndex = fieldsIndex === 0 ? 2 : (fieldsIndex - 1) % 3)} />
<div class="changer-down" on:click={() => (fieldsIndex = (fieldsIndex + 1) % 3)} />
</div>
</div>
</div>
</div>

View File

@ -1,38 +1,49 @@
<script>
import { onMount } from "svelte";
import Chrome from "./Chrome.svelte";
import {createEventDispatcher} from "svelte";
const dispatch = createEventDispatcher();
export let color = "#1ec131";
export let fieldsIndex = 0; // 0 hex 1 rgba 2 hsla
export let mode = 1; // 1 input点击模式 2 直接显示
export let width = "200px";
import { onMount } from 'svelte'
import Chrome from './Chrome.svelte'
import { createEventDispatcher } from 'svelte'
const dispatch = createEventDispatcher()
let active = false;
export let color = '#1ec131'
export let fieldsIndex = 0 // 0 hex 1 rgba 2 hsla
export let mode = 1 // 1 input点击模式 2 直接显示
export let width = '200px'
let panelX = 0;
let panelY = 0;
let transformX = 0;
onMount(() => {});
let active = false
let panelX = 0
let panelY = 0
let transformX = 0
onMount(() => {})
let focusStatus = false
function openChrome(event) {
panelX = event.clientX - event.offsetX;;
panelY = event.clientY;
transformX = event.target.offsetWidth + 5;
focusStatus = true
if (!active) {
active = true;
panelX = event.clientX - event.offsetX
panelY = event.clientY
transformX = event.target.offsetWidth + 5
setTimeout(()=>{
active = true
},100)
}
}
function closeChrome() {
active = false;
if (!focusStatus) {
active = false
}
}
const handleInput = event => {
color = event.detail;
dispatch("change", event.detail);
};
color = event.detail
dispatch('change', event.detail)
}
const mouseupEvent = event => {
color = event.detail
dispatch('changeEnd', event.detail)
}
</script>
<style lang="scss">
@ -41,32 +52,29 @@
position: relative;
}
.smx-color-picker input{
.smx-color-picker input {
width: 100%;
}
.smx-color-panel {
position: fixed;
z-index: 9999;
}
.smx-color-panel {
position: fixed;
z-index: 9999;
}
</style>
<svelte:window on:click={closeChrome}/>
{#if mode === 1}
<div class="smx-color-picker" style="width: {width}">
<input
type="text"
class="input"
on:click|stopPropagation={openChrome}
value={color}
style="background: {color};"
readonly />
{#if active}
<div class="smx-color-panel" on:click|stopPropagation style="top: {panelY}px;left:{panelX}px;transform: translate({transformX}px, -50%)">
<Chrome startColor={color} on:input={handleInput} fieldsIndex={fieldsIndex}/>
</div>
{/if}
</div>
{:else}
<Chrome startColor={color} on:input={handleInput} fieldsIndex={fieldsIndex}/>
{/if}
<svelte:window on:click={closeChrome} />
{#if mode === 1}
<div class="smx-color-picker" style="width: {width}">
<input type="text" class="input" on:focus={openChrome} on:blur={()=>focusStatus = false} value={color} style="background: {color};" readonly />
{#if active}
<div
class="smx-color-panel"
on:click|stopPropagation
style="top: {panelY}px;left:{panelX}px;transform: translate({transformX}px, -50%)">
<Chrome startColor={color} on:input={handleInput} {fieldsIndex} on:mouseupEvent={mouseupEvent}/>
</div>
{/if}
</div>
{:else}
<Chrome startColor={color} on:input={handleInput} {fieldsIndex} on:mouseupEvent={mouseupEvent}/>
{/if}