This commit is contained in:
xuziqiang
2024-05-15 17:29:42 +08:00
commit d0155dbe3c
7296 changed files with 1832517 additions and 0 deletions

View File

@@ -0,0 +1,138 @@
<template>
<div class="vc-alpha">
<div class="vc-alpha-checkboard-wrap">
<checkboard></checkboard>
</div>
<div class="vc-alpha-gradient" :style="{ background: gradientColor }"></div>
<div
class="vc-alpha-container"
ref="container"
@mousedown="handleMouseDown"
@touchmove="handleChange"
@touchstart="handleChange"
>
<div class="vc-alpha-pointer" :style="{ left: colors.a * 100 + '%' }">
<div class="vc-alpha-picker"></div>
</div>
</div>
</div>
</template>
<script>
import checkboard from './Checkboard.vue';
export default {
name: 'Alpha',
props: {
value: Object,
onChange: Function,
},
components: {
checkboard,
},
computed: {
colors() {
return this.value;
},
gradientColor() {
var rgba = this.colors.rgba;
var rgbStr = [rgba.r, rgba.g, rgba.b].join(',');
return (
'linear-gradient(to right, rgba(' + rgbStr + ', 0) 0%, rgba(' + rgbStr + ', 1) 100%)'
);
},
},
methods: {
handleChange(e, skip) {
!skip && e.preventDefault();
var container = this.$refs.container;
if (!container) {
// for some edge cases, container may not exist. see #220
return;
}
var containerWidth = container.clientWidth;
var xOffset = container.getBoundingClientRect().left + window.pageXOffset;
var pageX = e.pageX || (e.touches ? e.touches[0].pageX : 0);
var left = pageX - xOffset;
var a;
if (left < 0) {
a = 0;
} else if (left > containerWidth) {
a = 1;
} else {
a = Math.round((left * 100) / containerWidth) / 100;
}
if (this.colors.a !== a) {
this.$emit('change', {
h: this.colors.hsl.h,
s: this.colors.hsl.s,
l: this.colors.hsl.l,
a: a,
source: 'rgba',
});
}
},
handleMouseDown(e) {
this.handleChange(e, true);
window.addEventListener('mousemove', this.handleChange);
window.addEventListener('mouseup', this.handleMouseUp);
},
handleMouseUp() {
this.unbindEventListeners();
},
unbindEventListeners() {
window.removeEventListener('mousemove', this.handleChange);
window.removeEventListener('mouseup', this.handleMouseUp);
},
},
};
</script>
<style>
.vc-alpha {
position: absolute;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
}
.vc-alpha-checkboard-wrap {
position: absolute;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
overflow: hidden;
}
.vc-alpha-gradient {
position: absolute;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
}
.vc-alpha-container {
cursor: pointer;
position: relative;
z-index: 2;
height: 100%;
margin: 0 3px;
}
.vc-alpha-pointer {
z-index: 2;
position: absolute;
}
.vc-alpha-picker {
cursor: pointer;
width: 4px;
border-radius: 1px;
height: 8px;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
background: #fff;
margin-top: 1px;
transform: translateX(-2px);
}
</style>

View File

@@ -0,0 +1,92 @@
<template>
<div class="vc-checkerboard" :style="bgStyle"></div>
</template>
<script>
let _checkboardCache = {};
export default {
name: 'Checkboard',
props: {
size: {
type: [Number, String],
default: 8,
},
white: {
type: String,
default: '#fff',
},
grey: {
type: String,
default: '#e6e6e6',
},
},
computed: {
bgStyle() {
return {
'background-image': 'url(' + getCheckboard(this.white, this.grey, this.size) + ')',
};
},
},
};
/**
* get base 64 data by canvas
*
* @param {String} c1 hex color
* @param {String} c2 hex color
* @param {Number} size
*/
function renderCheckboard(c1, c2, size) {
// Dont Render On Server
if (typeof document === 'undefined') {
return null;
}
var canvas = document.createElement('canvas');
canvas.width = canvas.height = size * 2;
var ctx = canvas.getContext('2d');
// If no context can be found, return early.
if (!ctx) {
return null;
}
ctx.fillStyle = c1;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = c2;
ctx.fillRect(0, 0, size, size);
ctx.translate(size, size);
ctx.fillRect(0, 0, size, size);
return canvas.toDataURL();
}
/**
* get checkboard base data and cache
*
* @param {String} c1 hex color
* @param {String} c2 hex color
* @param {Number} size
*/
function getCheckboard(c1, c2, size) {
var key = c1 + ',' + c2 + ',' + size;
if (_checkboardCache[key]) {
return _checkboardCache[key];
} else {
var checkboard = renderCheckboard(c1, c2, size);
_checkboardCache[key] = checkboard;
return checkboard;
}
}
</script>
<style>
.vc-checkerboard {
position: absolute;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
background-size: contain;
}
</style>

View File

@@ -0,0 +1,114 @@
<template>
<div class="vc-editable-input">
<input
:aria-labelledby="labelId"
class="vc-input__input"
v-model="val"
@keydown="handleKeyDown"
@input="update"
ref="input"
/>
<span :for="label" class="vc-input__label" :id="labelId">{{ labelSpanText }}</span>
<span class="vc-input__desc">{{ desc }}</span>
</div>
</template>
<script>
export default {
name: 'editableInput',
props: {
label: String,
labelText: String,
desc: String,
value: [String, Number],
max: Number,
min: Number,
arrowOffset: {
type: Number,
default: 1,
},
},
computed: {
val: {
get() {
return this.value;
},
set(v) {
// TODO: min
if (!(this.max === undefined) && +v > this.max) {
this.$refs.input.value = this.max;
} else {
return v;
}
},
},
labelId() {
return `input__label__${this.label}__${Math.random().toString().slice(2, 5)}`;
},
labelSpanText() {
return this.labelText || this.label;
},
},
methods: {
update(e) {
this.handleChange(e.target.value);
},
handleChange(newVal) {
let data = {};
data[this.label] = newVal;
if (data.hex === undefined && data['#'] === undefined) {
this.$emit('change', data);
} else if (newVal.length > 5) {
this.$emit('change', data);
}
},
// **** unused
// handleBlur (e) {
// console.log(e)
// },
handleKeyDown(e) {
let val = this.val;
let number = Number(val);
if (number) {
let amount = this.arrowOffset || 1;
// Up
if (e.keyCode === 38) {
val = number + amount;
this.handleChange(val);
e.preventDefault();
}
// Down
if (e.keyCode === 40) {
val = number - amount;
this.handleChange(val);
e.preventDefault();
}
}
},
// **** unused
// handleDrag (e) {
// console.log(e)
// },
// handleMouseDown (e) {
// console.log(e)
// }
},
};
</script>
<style>
.vc-editable-input {
position: relative;
}
.vc-input__input {
padding: 0;
border: 0;
outline: none;
}
.vc-input__label {
text-transform: capitalize;
}
</style>

View File

@@ -0,0 +1,205 @@
<template>
<div :class="['vc-hue', directionClass]">
<div
class="vc-hue-container"
role="slider"
:aria-valuenow="colors.hsl.h"
aria-valuemin="0"
aria-valuemax="360"
ref="container"
@mousedown="handleMouseDown"
@touchmove="handleChange"
@touchstart="handleChange"
>
<div
class="vc-hue-pointer"
:style="{ top: pointerTop, left: pointerLeft }"
role="presentation"
>
<div class="vc-hue-picker"></div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Hue',
props: {
value: Object,
direction: {
type: String,
// [horizontal | vertical]
default: 'horizontal',
},
},
data() {
return {
oldHue: 0,
pullDirection: '',
};
},
computed: {
colors() {
const h = this.value.hsl.h;
if (h !== 0 && h - this.oldHue > 0) this.pullDirection = 'right';
if (h !== 0 && h - this.oldHue < 0) this.pullDirection = 'left';
this.oldHue = h;
return this.value;
},
directionClass() {
return {
'vc-hue--horizontal': this.direction === 'horizontal',
'vc-hue--vertical': this.direction === 'vertical',
};
},
pointerTop() {
if (this.direction === 'vertical') {
if (this.colors.hsl.h === 0 && this.pullDirection === 'right') return 0;
return -((this.colors.hsl.h * 100) / 360) + 100 + '%';
} else {
return 0;
}
},
pointerLeft() {
if (this.direction === 'vertical') {
return 0;
} else {
if (this.colors.hsl.h === 0 && this.pullDirection === 'right') return '100%';
return (this.colors.hsl.h * 100) / 360 + '%';
}
},
},
methods: {
handleChange(e, skip) {
!skip && e.preventDefault();
var container = this.$refs.container;
if (!container) {
// for some edge cases, container may not exist. see #220
return;
}
var containerWidth = container.clientWidth;
var containerHeight = container.clientHeight;
var xOffset = container.getBoundingClientRect().left + window.pageXOffset;
var yOffset = container.getBoundingClientRect().top + window.pageYOffset;
var pageX = e.pageX || (e.touches ? e.touches[0].pageX : 0);
var pageY = e.pageY || (e.touches ? e.touches[0].pageY : 0);
var left = pageX - xOffset;
var top = pageY - yOffset;
var h;
var percent;
if (this.direction === 'vertical') {
if (top < 0) {
h = 360;
} else if (top > containerHeight) {
h = 0;
} else {
percent = -((top * 100) / containerHeight) + 100;
h = (360 * percent) / 100;
}
if (this.colors.hsl.h !== h) {
this.$emit('change', {
h: h,
s: this.colors.hsl.s,
l: this.colors.hsl.l,
a: this.colors.hsl.a,
source: 'hsl',
});
}
} else {
if (left < 0) {
h = 0;
} else if (left > containerWidth) {
h = 360;
} else {
percent = (left * 100) / containerWidth;
h = (360 * percent) / 100;
}
if (this.colors.hsl.h !== h) {
this.$emit('change', {
h: h,
s: this.colors.hsl.s,
l: this.colors.hsl.l,
a: this.colors.hsl.a,
source: 'hsl',
});
}
}
},
handleMouseDown(e) {
this.handleChange(e, true);
window.addEventListener('mousemove', this.handleChange);
window.addEventListener('mouseup', this.handleMouseUp);
},
handleMouseUp(e) {
this.unbindEventListeners();
},
unbindEventListeners() {
window.removeEventListener('mousemove', this.handleChange);
window.removeEventListener('mouseup', this.handleMouseUp);
},
},
};
</script>
<style>
.vc-hue {
position: absolute;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
border-radius: 2px;
}
.vc-hue--horizontal {
background: linear-gradient(
to right,
#f00 0%,
#ff0 17%,
#0f0 33%,
#0ff 50%,
#00f 67%,
#f0f 83%,
#f00 100%
);
}
.vc-hue--vertical {
background: linear-gradient(
to top,
#f00 0%,
#ff0 17%,
#0f0 33%,
#0ff 50%,
#00f 67%,
#f0f 83%,
#f00 100%
);
}
.vc-hue-container {
cursor: pointer;
margin: 0 2px;
position: relative;
height: 100%;
}
.vc-hue-pointer {
z-index: 2;
position: absolute;
}
.vc-hue-picker {
cursor: pointer;
margin-top: 1px;
width: 4px;
border-radius: 1px;
height: 20px;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
background: #fff;
transform: translateX(-2px);
}
</style>

View File

@@ -0,0 +1,132 @@
<template>
<div
class="vc-saturation"
:style="{ background: bgColor }"
ref="container"
@mousedown="handleMouseDown"
@touchmove="handleChange"
@touchstart="handleChange"
>
<div class="vc-saturation--white"></div>
<div class="vc-saturation--black"></div>
<div class="vc-saturation-pointer" :style="{ top: pointerTop, left: pointerLeft }">
<div class="vc-saturation-circle"></div>
</div>
</div>
</template>
<script>
import clamp from 'clamp';
import { throttle } from 'lodash-es';
export default {
name: 'Saturation',
props: {
value: Object,
},
computed: {
colors() {
return this.value;
},
bgColor() {
return `hsl(${this.colors.hsv.h}, 100%, 50%)`;
},
pointerTop() {
return -(this.colors.hsv.v * 100) + 1 + 100 + '%';
},
pointerLeft() {
return this.colors.hsv.s * 100 + '%';
},
},
methods: {
throttle: throttle(
(fn, data) => {
fn(data);
},
20,
{
leading: true,
trailing: false,
}
),
handleChange(e, skip) {
!skip && e.preventDefault();
var container = this.$refs.container;
if (!container) {
// for some edge cases, container may not exist. see #220
return;
}
var containerWidth = container.clientWidth;
var containerHeight = container.clientHeight;
var xOffset = container.getBoundingClientRect().left + window.pageXOffset;
var yOffset = container.getBoundingClientRect().top + window.pageYOffset;
var pageX = e.pageX || (e.touches ? e.touches[0].pageX : 0);
var pageY = e.pageY || (e.touches ? e.touches[0].pageY : 0);
var left = clamp(pageX - xOffset, 0, containerWidth);
var top = clamp(pageY - yOffset, 0, containerHeight);
var saturation = left / containerWidth;
var bright = clamp(-(top / containerHeight) + 1, 0, 1);
this.throttle(this.onChange, {
h: this.colors.hsv.h,
s: saturation,
v: bright,
a: this.colors.hsv.a,
source: 'hsva',
});
},
onChange(param) {
this.$emit('change', param);
},
handleMouseDown(e) {
// this.handleChange(e, true)
window.addEventListener('mousemove', this.handleChange);
window.addEventListener('mouseup', this.handleChange);
window.addEventListener('mouseup', this.handleMouseUp);
},
handleMouseUp(e) {
this.unbindEventListeners();
},
unbindEventListeners() {
window.removeEventListener('mousemove', this.handleChange);
window.removeEventListener('mouseup', this.handleChange);
window.removeEventListener('mouseup', this.handleMouseUp);
},
},
created() {},
};
</script>
<style>
.vc-saturation,
.vc-saturation--white,
.vc-saturation--black {
cursor: pointer;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.vc-saturation--white {
background: linear-gradient(to right, #fff, rgba(255, 255, 255, 0));
}
.vc-saturation--black {
background: linear-gradient(to top, #000, rgba(0, 0, 0, 0));
}
.vc-saturation-pointer {
cursor: pointer;
position: absolute;
}
.vc-saturation-circle {
cursor: head;
width: 4px;
height: 4px;
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(-2px, -2px);
}
</style>