mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
添加取色器组件
This commit is contained in:
parent
2186fc0d97
commit
b71b78f9f1
11
package-lock.json
generated
11
package-lock.json
generated
@ -1935,6 +1935,12 @@
|
||||
"integrity": "sha1-qcpLcKGLJwzLK8Cqr+/R1Ia36nQ=",
|
||||
"dev": true
|
||||
},
|
||||
"@types/tinycolor2": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npm.taobao.org/@types/tinycolor2/download/@types/tinycolor2-1.4.2.tgz",
|
||||
"integrity": "sha1-chylxdGimItKiG41wv/Fc1tq+98=",
|
||||
"dev": true
|
||||
},
|
||||
"@types/uglify-js": {
|
||||
"version": "3.11.1",
|
||||
"resolved": "https://registry.npm.taobao.org/@types/uglify-js/download/@types/uglify-js-3.11.1.tgz?cache=0&sync_timestamp=1605057452755&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fuglify-js%2Fdownload%2F%40types%2Fuglify-js-3.11.1.tgz",
|
||||
@ -15318,6 +15324,11 @@
|
||||
"resolved": "https://registry.npm.taobao.org/tiny-emitter/download/tiny-emitter-2.1.0.tgz",
|
||||
"integrity": "sha1-HRpW7fxRxD6GPLtTgqcjMONVVCM="
|
||||
},
|
||||
"tinycolor2": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npm.taobao.org/tinycolor2/download/tinycolor2-1.4.2.tgz",
|
||||
"integrity": "sha1-P2pNEHGtB2dtf6Ry4frECnGdiAM="
|
||||
},
|
||||
"tmp": {
|
||||
"version": "0.0.33",
|
||||
"resolved": "https://registry.npm.taobao.org/tmp/download/tmp-0.0.33.tgz",
|
||||
|
@ -28,6 +28,7 @@
|
||||
"prosemirror-state": "^1.3.3",
|
||||
"prosemirror-view": "^1.16.4",
|
||||
"store2": "^2.12.0",
|
||||
"tinycolor2": "^1.4.2",
|
||||
"vue": "^3.0.0",
|
||||
"vuedraggable": "^4.0.1",
|
||||
"vuex": "^4.0.0-0"
|
||||
@ -45,6 +46,7 @@
|
||||
"@types/prosemirror-schema-basic": "^1.0.1",
|
||||
"@types/prosemirror-schema-list": "^1.0.1",
|
||||
"@types/resize-observer-browser": "^0.1.4",
|
||||
"@types/tinycolor2": "^1.4.2",
|
||||
"@typescript-eslint/eslint-plugin": "^2.33.0",
|
||||
"@typescript-eslint/parser": "^2.33.0",
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
|
131
src/components/ColorPicker/Alpha.vue
Normal file
131
src/components/ColorPicker/Alpha.vue
Normal file
@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<div class="alpha">
|
||||
<div class="alpha-checkboard-wrap">
|
||||
<Checkboard />
|
||||
</div>
|
||||
<div class="alpha-gradient" :style="{ background: gradientColor }"></div>
|
||||
<div
|
||||
class="alpha-container"
|
||||
ref="alphaRef"
|
||||
@mousedown="$event => handleMouseDown($event)"
|
||||
>
|
||||
<div class="alpha-pointer" :style="{ left: color.a * 100 + '%' }">
|
||||
<div class="alpha-picker"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onUnmounted, PropType, ref } from 'vue'
|
||||
|
||||
import Checkboard from './Checkboard.vue'
|
||||
import { ColorFormats } from 'tinycolor2'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'alpha',
|
||||
components: {
|
||||
Checkboard,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Object as PropType<ColorFormats.RGBA>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const color = computed(() => props.modelValue)
|
||||
const gradientColor = computed(() => {
|
||||
const rgbaStr = [color.value.r, color.value.g, color.value.b].join(',')
|
||||
return `linear-gradient(to right, rgba(${rgbaStr}, 0) 0%, rgba(${rgbaStr}, 1) 100%)`
|
||||
})
|
||||
|
||||
const alphaRef = ref<HTMLElement | null>(null)
|
||||
const handleChange = (e: MouseEvent) => {
|
||||
e.preventDefault()
|
||||
if(!alphaRef.value) return
|
||||
const containerWidth = alphaRef.value.clientWidth
|
||||
const xOffset = alphaRef.value.getBoundingClientRect().left + window.pageXOffset
|
||||
const left = e.pageX - xOffset
|
||||
let a
|
||||
|
||||
if(left < 0) a = 0
|
||||
else if(left > containerWidth) a = 1
|
||||
else a = Math.round(left * 100 / containerWidth) / 100
|
||||
|
||||
if(color.value.a !== a) {
|
||||
emit('update:modelValue', {
|
||||
r: color.value.r,
|
||||
g: color.value.g,
|
||||
b: color.value.b,
|
||||
a: a,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const unbindEventListeners = () => {
|
||||
window.removeEventListener('mousemove', handleChange)
|
||||
window.removeEventListener('mouseup', unbindEventListeners)
|
||||
}
|
||||
const handleMouseDown = (e: MouseEvent) => {
|
||||
handleChange(e)
|
||||
window.addEventListener('mousemove', handleChange)
|
||||
window.addEventListener('mouseup', unbindEventListeners)
|
||||
}
|
||||
|
||||
onUnmounted(unbindEventListeners)
|
||||
|
||||
return {
|
||||
alphaRef,
|
||||
gradientColor,
|
||||
handleMouseDown,
|
||||
color,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.alpha {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
.alpha-checkboard-wrap {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.alpha-gradient {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
.alpha-container {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
height: 100%;
|
||||
margin: 0 3px;
|
||||
}
|
||||
.alpha-pointer {
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
}
|
||||
.alpha-picker {
|
||||
cursor: pointer;
|
||||
width: 4px;
|
||||
height: 8px;
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, .6);
|
||||
background: #fff;
|
||||
margin-top: 1px;
|
||||
transform: translateX(-2px);
|
||||
}
|
||||
</style>
|
72
src/components/ColorPicker/Checkboard.vue
Normal file
72
src/components/ColorPicker/Checkboard.vue
Normal file
@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div class="checkerboard" :style="bgStyle"></div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue'
|
||||
|
||||
const checkboardCache = {}
|
||||
const renderCheckboard = (white: string, grey: string, size: number) => {
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = canvas.height = size * 2
|
||||
const ctx = canvas.getContext('2d')
|
||||
|
||||
if(!ctx) return null
|
||||
|
||||
ctx.fillStyle = white
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
||||
ctx.fillStyle = grey
|
||||
ctx.fillRect(0, 0, size, size)
|
||||
ctx.translate(size, size)
|
||||
ctx.fillRect(0, 0, size, size)
|
||||
return canvas.toDataURL()
|
||||
}
|
||||
|
||||
const getCheckboard = (white: string, grey: string, size: number) => {
|
||||
const key = white + ',' + grey + ',' + size
|
||||
if(checkboardCache[key]) return checkboardCache[key]
|
||||
|
||||
const checkboard = renderCheckboard(white, grey, size)
|
||||
checkboardCache[key] = checkboard
|
||||
return checkboard
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'checkboard',
|
||||
props: {
|
||||
size: {
|
||||
type: Number,
|
||||
default: 8,
|
||||
},
|
||||
white: {
|
||||
type: String,
|
||||
default: '#fff',
|
||||
},
|
||||
grey: {
|
||||
type: String,
|
||||
default: '#e6e6e6',
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const bgStyle = computed(() => {
|
||||
const checkboard = getCheckboard(props.white, props.grey, props.size)
|
||||
return { backgroundImage: `url(${checkboard})` }
|
||||
})
|
||||
|
||||
return {
|
||||
bgStyle,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.checkerboard {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-size: contain;
|
||||
}
|
||||
</style>
|
63
src/components/ColorPicker/EditableInput.vue
Normal file
63
src/components/ColorPicker/EditableInput.vue
Normal file
@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="editable-input">
|
||||
<input
|
||||
class="input-content"
|
||||
:value="val"
|
||||
@input="$event => handleInput($event)"
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, PropType } from 'vue'
|
||||
import tinycolor, { ColorFormats } from 'tinycolor2'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'editable-input',
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Object as PropType<ColorFormats.RGBA>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const val = computed(() => {
|
||||
let _hex = ''
|
||||
if(props.modelValue.a < 1) _hex = tinycolor(props.modelValue).toHex8String().toUpperCase()
|
||||
else _hex = tinycolor(props.modelValue).toHexString().toUpperCase()
|
||||
return _hex.replace('#', '')
|
||||
})
|
||||
|
||||
const handleInput = (e: InputEvent) => {
|
||||
const value = (e.target as HTMLInputElement).value
|
||||
if(value.length >= 6) emit('update:modelValue', tinycolor(value).toRgb())
|
||||
}
|
||||
|
||||
return {
|
||||
val,
|
||||
handleInput,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.editable-input {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
}
|
||||
.input-content {
|
||||
width: 100%;
|
||||
padding: 3px;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #ddd;
|
||||
outline: none;
|
||||
text-align: center;
|
||||
}
|
||||
.input-label {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
</style>
|
127
src/components/ColorPicker/Hue.vue
Normal file
127
src/components/ColorPicker/Hue.vue
Normal file
@ -0,0 +1,127 @@
|
||||
<template>
|
||||
<div class="hue">
|
||||
<div
|
||||
class="hue-container"
|
||||
ref="hueRef"
|
||||
@mousedown="$event => handleMouseDown($event)"
|
||||
>
|
||||
<div
|
||||
class="hue-pointer"
|
||||
:style="{ left: pointerLeft }"
|
||||
>
|
||||
<div class="hue-picker"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onUnmounted, PropType, ref, watch } from 'vue'
|
||||
import tinycolor, { ColorFormats } from 'tinycolor2'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'hue',
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Object as PropType<ColorFormats.RGBA>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const oldHue = ref(0)
|
||||
const pullDirection = ref('')
|
||||
|
||||
const color = computed(() => tinycolor(props.modelValue).toHsl())
|
||||
|
||||
const pointerLeft = computed(() => {
|
||||
if(color.value.h === 0 && pullDirection.value === 'right') return '100%'
|
||||
return color.value.h * 100 / 360 + '%'
|
||||
})
|
||||
|
||||
watch(() => props.modelValue, () => {
|
||||
const hsl = tinycolor(props.modelValue).toHsl()
|
||||
const h = hsl.h
|
||||
if(h !== 0 && h - oldHue.value > 0) pullDirection.value = 'right'
|
||||
if(h !== 0 && h - oldHue.value < 0) pullDirection.value = 'left'
|
||||
oldHue.value = h
|
||||
})
|
||||
|
||||
const hueRef = ref<HTMLElement | null>(null)
|
||||
const handleChange = (e: MouseEvent) => {
|
||||
e.preventDefault()
|
||||
if(!hueRef.value) return
|
||||
|
||||
const containerWidth = hueRef.value.clientWidth
|
||||
const xOffset = hueRef.value.getBoundingClientRect().left + window.pageXOffset
|
||||
const left = e.pageX - xOffset
|
||||
let h, percent
|
||||
|
||||
if(left < 0) h = 0
|
||||
else if(left > containerWidth) h = 360
|
||||
else {
|
||||
percent = left * 100 / containerWidth
|
||||
h = (360 * percent / 100)
|
||||
}
|
||||
if(color.value.h !== h) {
|
||||
const rgba = tinycolor({
|
||||
h,
|
||||
l: color.value.l,
|
||||
s: color.value.s,
|
||||
a: color.value.a,
|
||||
}).toRgb()
|
||||
|
||||
emit('update:modelValue', rgba)
|
||||
}
|
||||
}
|
||||
|
||||
const unbindEventListeners = () => {
|
||||
window.removeEventListener('mousemove', handleChange)
|
||||
window.removeEventListener('mouseup', unbindEventListeners)
|
||||
}
|
||||
const handleMouseDown = (e: MouseEvent) => {
|
||||
handleChange(e)
|
||||
window.addEventListener('mousemove', handleChange)
|
||||
window.addEventListener('mouseup', unbindEventListeners)
|
||||
}
|
||||
|
||||
onUnmounted(unbindEventListeners)
|
||||
|
||||
return {
|
||||
hueRef,
|
||||
handleMouseDown,
|
||||
pointerLeft,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.hue {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
|
||||
}
|
||||
.hue-container {
|
||||
cursor: pointer;
|
||||
margin: 0 2px;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
.hue-pointer {
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
.hue-picker {
|
||||
cursor: pointer;
|
||||
margin-top: 1px;
|
||||
width: 4px;
|
||||
height: 8px;
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, .6);
|
||||
background: #fff;
|
||||
transform: translateX(-2px);
|
||||
}
|
||||
</style>
|
122
src/components/ColorPicker/Saturation.vue
Normal file
122
src/components/ColorPicker/Saturation.vue
Normal file
@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<div
|
||||
class="saturation"
|
||||
ref="saturationRef"
|
||||
:style="{ background: bgColor }"
|
||||
@mousedown="$event => handleMouseDown($event)"
|
||||
>
|
||||
<div class="saturation-white"></div>
|
||||
<div class="saturation-black"></div>
|
||||
<div class="saturation-pointer"
|
||||
:style="{
|
||||
top: pointerTop,
|
||||
left: pointerLeft,
|
||||
}"
|
||||
>
|
||||
<div class="saturation-circle"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onUnmounted, PropType, ref } from 'vue'
|
||||
import tinycolor, { ColorFormats } from 'tinycolor2'
|
||||
import throttle from 'lodash/throttle'
|
||||
import clamp from 'lodash/clamp'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'saturation',
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Object as PropType<ColorFormats.RGBA>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const color = computed(() => tinycolor(props.modelValue).toHsv())
|
||||
|
||||
const bgColor = computed(() => `hsl(${color.value.h}, 100%, 50%)`)
|
||||
const pointerTop = computed(() => (-(color.value.v * 100) + 1) + 100 + '%')
|
||||
const pointerLeft = computed(() => color.value.s * 100 + '%')
|
||||
|
||||
const emitChangeEvent = throttle(function(param) {
|
||||
emit('update:modelValue', param)
|
||||
}, 20, { leading: true, trailing: false })
|
||||
|
||||
const saturationRef = ref<HTMLElement | null>(null)
|
||||
const handleChange = (e: MouseEvent) => {
|
||||
e.preventDefault()
|
||||
if(!saturationRef.value) return
|
||||
|
||||
const containerWidth = saturationRef.value.clientWidth
|
||||
const containerHeight = saturationRef.value.clientHeight
|
||||
const xOffset = saturationRef.value.getBoundingClientRect().left + window.pageXOffset
|
||||
const yOffset = saturationRef.value.getBoundingClientRect().top + window.pageYOffset
|
||||
const left = clamp(e.pageX - xOffset, 0, containerWidth)
|
||||
const top = clamp(e.pageY - yOffset, 0, containerHeight)
|
||||
const saturation = left / containerWidth
|
||||
const bright = clamp(-(top / containerHeight) + 1, 0, 1)
|
||||
|
||||
const rgba = tinycolor({
|
||||
h: color.value.h,
|
||||
s: saturation,
|
||||
v: bright,
|
||||
a: color.value.a,
|
||||
}).toRgb()
|
||||
|
||||
emitChangeEvent(rgba)
|
||||
}
|
||||
|
||||
|
||||
const unbindEventListeners = () => {
|
||||
window.removeEventListener('mousemove', handleChange)
|
||||
window.removeEventListener('mouseup', unbindEventListeners)
|
||||
}
|
||||
const handleMouseDown = (e: MouseEvent) => {
|
||||
handleChange(e)
|
||||
window.addEventListener('mousemove', handleChange)
|
||||
window.addEventListener('mouseup', unbindEventListeners)
|
||||
}
|
||||
|
||||
onUnmounted(unbindEventListeners)
|
||||
|
||||
return {
|
||||
saturationRef,
|
||||
bgColor,
|
||||
handleMouseDown,
|
||||
pointerTop,
|
||||
pointerLeft,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.saturation,
|
||||
.saturation-white,
|
||||
.saturation-black {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.saturation-white {
|
||||
background: linear-gradient(to right, #fff, rgba(255, 255, 255, 0));
|
||||
}
|
||||
.saturation-black {
|
||||
background: linear-gradient(to top, #000, rgba(0, 0, 0, 0));
|
||||
}
|
||||
.saturation-pointer {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
}
|
||||
.saturation-circle {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
box-shadow: 0 0 0 1.5px #fff, inset 0 0 1px 1px rgba(0, 0, 0, .3), 0 0 1px 2px rgba(0, 0, 0, .4);
|
||||
border-radius: 50%;
|
||||
transform: translate(-2px, -2px);
|
||||
}
|
||||
</style>
|
239
src/components/ColorPicker/index.vue
Normal file
239
src/components/ColorPicker/index.vue
Normal file
@ -0,0 +1,239 @@
|
||||
<template>
|
||||
<div class="color-picker" @contextmenu.prevent>
|
||||
<div class="picker-saturation-wrap">
|
||||
<Saturation v-model="color" />
|
||||
</div>
|
||||
<div class="picker-controls">
|
||||
<div class="picker-color-wrap">
|
||||
<div class="picker-current-color" :style="{ background: currentColor }"></div>
|
||||
<Checkboard />
|
||||
</div>
|
||||
<div class="picker-sliders">
|
||||
<div class="picker-hue-wrap"><Hue v-model="color" /></div>
|
||||
<div class="picker-alpha-wrap"><Alpha v-model="color" /></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="picker-field"><EditableInput v-model="color" /></div>
|
||||
|
||||
<div class="picker-presets">
|
||||
<div
|
||||
class="picker-presets-color"
|
||||
v-for="c in themeColors"
|
||||
:key="c"
|
||||
:style="{background: c}"
|
||||
@click="selectPresetColor(c)"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="picker-gradient-presets">
|
||||
<div
|
||||
class="picker-gradient-col"
|
||||
v-for="(col, index) in presetColors"
|
||||
:key="index"
|
||||
>
|
||||
<div class="picker-gradient-color"
|
||||
v-for="c in col"
|
||||
:key="c"
|
||||
:style="{background: c}"
|
||||
@click="selectPresetColor(c)"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="picker-presets">
|
||||
<div
|
||||
v-for="c in standardColors"
|
||||
:key="c"
|
||||
class="picker-presets-color"
|
||||
:style="{ background: c }"
|
||||
@click="selectPresetColor(c)"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import tinycolor, { ColorFormats } from 'tinycolor2'
|
||||
|
||||
import Alpha from './Alpha.vue'
|
||||
import Checkboard from './Checkboard.vue'
|
||||
import Hue from './Hue.vue'
|
||||
import Saturation from './Saturation.vue'
|
||||
import EditableInput from './EditableInput.vue'
|
||||
|
||||
const presetColorConfig = [
|
||||
['#7f7f7f', '#f2f2f2'],
|
||||
['#0d0d0d', '#808080'],
|
||||
['#1c1a10', '#ddd8c3'],
|
||||
['#0e243d', '#c6d9f0'],
|
||||
['#233f5e', '#dae5f0'],
|
||||
['#632623', '#f2dbdb'],
|
||||
['#4d602c', '#eaf1de'],
|
||||
['#3f3150', '#e6e0ec'],
|
||||
['#1e5867', '#d9eef3'],
|
||||
['#99490f', '#fee9da'],
|
||||
]
|
||||
|
||||
const gradient = (startColor: string, endColor: string, step: number) => {
|
||||
const _startColor = tinycolor(startColor).toRgb()
|
||||
const _endColor = tinycolor(endColor).toRgb()
|
||||
|
||||
const rStep = (_endColor.r - _startColor.r) / step
|
||||
const gStep = (_endColor.g - _startColor.g) / step
|
||||
const bStep = (_endColor.b - _startColor.b) / step
|
||||
const gradientColorArr = []
|
||||
|
||||
for(let i = 0; i < step; i++) {
|
||||
const gradientColor = tinycolor({
|
||||
r: _startColor.r + rStep * i,
|
||||
g: _startColor.g + gStep * i,
|
||||
b: _startColor.b + bStep * i,
|
||||
}).toRgbString()
|
||||
gradientColorArr.push(gradientColor)
|
||||
}
|
||||
return gradientColorArr
|
||||
}
|
||||
|
||||
const getPresetColors = () => {
|
||||
const presetColors = []
|
||||
for(const color of presetColorConfig) {
|
||||
presetColors.push(gradient(color[1], color[0], 5))
|
||||
}
|
||||
return presetColors
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'color-picker',
|
||||
components: {
|
||||
Alpha,
|
||||
Checkboard,
|
||||
Hue,
|
||||
Saturation,
|
||||
EditableInput,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: '#e86b99',
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const color = computed({
|
||||
get() {
|
||||
return tinycolor(props.modelValue).toRgb()
|
||||
},
|
||||
set(rgba: ColorFormats.RGBA) {
|
||||
const rgbaString = `rgba(${[rgba.r, rgba.g, rgba.b, rgba.a].join(',')})`
|
||||
emit('update:modelValue', rgbaString)
|
||||
},
|
||||
})
|
||||
|
||||
const themeColors = ['#000000', '#ffffff', '#eeece1', '#1e497b', '#4e81bb', '#e2534d', '#9aba60', '#8165a0', '#47acc5', '#f9974c']
|
||||
const standardColors = ['#c21401', '#ff1e02', '#ffc12a', '#ffff3a', '#90cf5b', '#00af57', '#00afee', '#0071be', '#00215f', '#72349d']
|
||||
const presetColors = getPresetColors()
|
||||
|
||||
const currentColor = computed(() => {
|
||||
return `rgba(${[color.value.r, color.value.g, color.value.b, color.value.a].join(',')})`
|
||||
})
|
||||
|
||||
const selectPresetColor = (colorString: string) => {
|
||||
emit('update:modelValue', colorString)
|
||||
}
|
||||
|
||||
return {
|
||||
themeColors,
|
||||
standardColors,
|
||||
presetColors,
|
||||
color,
|
||||
currentColor,
|
||||
selectPresetColor,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.color-picker {
|
||||
position: relative;
|
||||
width: 240px;
|
||||
background: #fff;
|
||||
user-select: none;
|
||||
}
|
||||
.picker-saturation-wrap {
|
||||
width: 100%;
|
||||
padding-bottom: 50%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.picker-controls {
|
||||
display: flex;
|
||||
}
|
||||
.picker-sliders {
|
||||
padding: 4px 0;
|
||||
flex: 1;
|
||||
}
|
||||
.picker-hue-wrap {
|
||||
position: relative;
|
||||
height: 10px;
|
||||
}
|
||||
.picker-alpha-wrap {
|
||||
position: relative;
|
||||
height: 10px;
|
||||
margin-top: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.picker-color-wrap {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
position: relative;
|
||||
margin-top: 4px;
|
||||
margin-right: 4px;
|
||||
|
||||
.checkerboard {
|
||||
background-size: auto;
|
||||
}
|
||||
}
|
||||
.picker-current-color {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.picker-field {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.picker-presets {
|
||||
@include grid-layout-wrapper();
|
||||
}
|
||||
.picker-presets-color {
|
||||
@include grid-layout-item(10, 7%);
|
||||
|
||||
height: 0;
|
||||
padding-bottom: 7%;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
.picker-gradient-presets {
|
||||
@include grid-layout-wrapper();
|
||||
}
|
||||
.picker-gradient-col {
|
||||
@include grid-layout-item(10, 7%);
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.picker-gradient-color {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
padding-bottom: 100%;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
@ -1,33 +0,0 @@
|
||||
<template>
|
||||
<APopover trigger="click" overlayClassName="popover">
|
||||
<template v-slot:content>
|
||||
<slot name="content"></slot>
|
||||
</template>
|
||||
<slot></slot>
|
||||
</APopover>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Popover } from 'ant-design-vue'
|
||||
|
||||
export default {
|
||||
name: 'popover',
|
||||
components: {
|
||||
APopover: Popover,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.popover {
|
||||
padding-top: 6px;
|
||||
|
||||
.ant-popover-arrow {
|
||||
display: none;
|
||||
}
|
||||
.ant-popover-inner {
|
||||
border: 1px solid #eee;
|
||||
box-shadow: 3px 3px 3px rgba(#000, 0.15);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,13 +1,24 @@
|
||||
<template>
|
||||
<div class="slide-style-panel">
|
||||
slide-style-panel
|
||||
<ColorPicker v-model="color" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import ColorPicker from '@/components/ColorPicker/index.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'slide-style-panel',
|
||||
components: {
|
||||
ColorPicker,
|
||||
},
|
||||
setup() {
|
||||
const color = ref('#888')
|
||||
|
||||
return {
|
||||
color,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
Loading…
x
Reference in New Issue
Block a user