feat(iconPicker): 缓存工作空间
This commit is contained in:
parent
01a479bc9a
commit
2e51cb1fab
120
src/module/colorPicker/ColorBox.vue
Normal file
120
src/module/colorPicker/ColorBox.vue
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'ColorBox',
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Nullable } from '/@src/module/type'
|
||||||
|
import { computed, onMounted, ref } from "vue";
|
||||||
|
import { HSBToHEX, RGBSTo, RGBToHSB } from '/@src/module/colorPicker/colorUtil'
|
||||||
|
import ColorPicker from './ColorPicker.vue'
|
||||||
|
import { usePosition } from "/@src/use/usePosition";
|
||||||
|
|
||||||
|
interface BoxProps {
|
||||||
|
color?: string
|
||||||
|
size?: Nullable<string>
|
||||||
|
alpha?: boolean
|
||||||
|
format?: 'hex' | 'rgb'
|
||||||
|
predefine?: boolean
|
||||||
|
colors?: string[],
|
||||||
|
}
|
||||||
|
|
||||||
|
const colorBoxProps = withDefaults(defineProps<BoxProps>(), {
|
||||||
|
color: '',
|
||||||
|
size: () => null,
|
||||||
|
alpha: false,
|
||||||
|
format: 'hex',
|
||||||
|
predefine: false,
|
||||||
|
colors: () => [
|
||||||
|
//默认预定义颜色列表
|
||||||
|
'#009688',
|
||||||
|
'#5FB878',
|
||||||
|
'#1E9FFF',
|
||||||
|
'#FF5722',
|
||||||
|
'#FFB800',
|
||||||
|
'#01AAED',
|
||||||
|
'#999',
|
||||||
|
'#c00',
|
||||||
|
'#ff8c00',
|
||||||
|
'#ffd700',
|
||||||
|
'#90ee90',
|
||||||
|
'#00ced1',
|
||||||
|
'#1e90ff',
|
||||||
|
'#c71585',
|
||||||
|
'rgb(0, 186, 189)',
|
||||||
|
'rgb(255, 120, 0)',
|
||||||
|
'rgb(250, 212, 0)',
|
||||||
|
'#393D49',
|
||||||
|
'rgba(0,0,0,.5)',
|
||||||
|
'rgba(255, 69, 0, 0.68)',
|
||||||
|
'rgba(144, 240, 144, 0.5)',
|
||||||
|
'rgba(31, 147, 255, 0.73)',
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const triggerSpanStyle = computed(() => {
|
||||||
|
let bgstr = ''
|
||||||
|
if (colorBoxProps.color) {
|
||||||
|
bgstr = colorBoxProps.color
|
||||||
|
|
||||||
|
if ((colorBoxProps.color.match(/[0-9]{1,3}/g) || []).length > 3) {
|
||||||
|
//需要优化
|
||||||
|
if (!(colorBoxProps.alpha && colorBoxProps.format == 'rgb')) {
|
||||||
|
bgstr = '#' + HSBToHEX(RGBToHSB(RGBSTo(colorBoxProps.color)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
background: bgstr,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const colorPickerWrapper = computed(() => {
|
||||||
|
return colorBoxProps.size
|
||||||
|
? `layui-colorpicker-${colorBoxProps.size}`
|
||||||
|
: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const colorBoxRefEl = ref<HTMLElement | null>(null)
|
||||||
|
const colorPickerRefEl = ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
console.log('colorPickerRefEl =>>>', colorPickerRefEl.value.teleportRefEl)
|
||||||
|
usePosition(colorBoxRefEl.value, colorPickerRefEl.value.teleportRefEl)
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="layui-unselect layui-colorpicker" ref="colorBoxRefEl">
|
||||||
|
<span
|
||||||
|
:class="[
|
||||||
|
{
|
||||||
|
'layui-colorpicker-trigger-bgcolor':
|
||||||
|
format == 'rgb' && alpha,
|
||||||
|
},
|
||||||
|
size ? colorPickerWrapper : ''
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="layui-colorpicker-trigger-span"
|
||||||
|
:style="triggerSpanStyle"
|
||||||
|
>
|
||||||
|
<!-- ICON_PICKER_DOWN = 'layui-icon-down', ICON_PICKER_CLOSE = 'layui-icon-close' -->
|
||||||
|
<i
|
||||||
|
:class="[
|
||||||
|
'layui-icon layui-colorpicker-trigger-i',
|
||||||
|
color ? 'layui-icon-down' : 'layui-icon-close',
|
||||||
|
]"
|
||||||
|
></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<ColorPicker
|
||||||
|
:visible="true"
|
||||||
|
:alpha="alpha"
|
||||||
|
:predefine="predefine"
|
||||||
|
ref="colorPickerRefEl"
|
||||||
|
></ColorPicker>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style scoped lang="less"></style>
|
91
src/module/colorPicker/ColorPicker.vue
Normal file
91
src/module/colorPicker/ColorPicker.vue
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'ColorPicker',
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
interface CProps {
|
||||||
|
visible: boolean
|
||||||
|
alpha: boolean
|
||||||
|
predefine: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<CProps>()
|
||||||
|
|
||||||
|
const domRefEl = ref<HTMLElement | null>(null)
|
||||||
|
defineExpose({
|
||||||
|
teleportRefEl: domRefEl
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<teleport to="body">
|
||||||
|
<div
|
||||||
|
v-if="visible"
|
||||||
|
class="layui-anim layui-anim-downbit layui-colorpicker-main"
|
||||||
|
ref="domRefEl"
|
||||||
|
>
|
||||||
|
<!-- //颜色面板-->
|
||||||
|
<div class="layui-colorpicker-main-wrapper">
|
||||||
|
<div class="layui-colorpicker-basis">
|
||||||
|
<div class="layui-colorpicker-basis-white"></div>
|
||||||
|
<div class="layui-colorpicker-basis-black"></div>
|
||||||
|
<div class="layui-colorpicker-basis-cursor"></div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-colorpicker-side">
|
||||||
|
<div class="layui-colorpicker-side-slider"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- //透明度条块-->
|
||||||
|
<div
|
||||||
|
:class="[
|
||||||
|
{
|
||||||
|
'layui-colorpicker-main-alpha': true,
|
||||||
|
'layui-show': alpha,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<div class="layui-colorpicker-alpha-bgcolor">
|
||||||
|
<div class="layui-colorpicker-alpha-slider"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- //预设颜色列表-->
|
||||||
|
<div v-if="predefine" class="layui-colorpicker-main-pre">
|
||||||
|
<div
|
||||||
|
v-for="c in colors"
|
||||||
|
:key="c"
|
||||||
|
:class="{
|
||||||
|
'layui-colorpicker-pre': true,
|
||||||
|
'layui-colorpicker-pre-isalpha':
|
||||||
|
(c.match(/[0-9]{1,3}/g) || []).length > 3,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div :style="{ background: c }"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- //底部表单元素区域-->
|
||||||
|
<div class="layui-colorpicker-main-input">
|
||||||
|
<div class="layui-inline">
|
||||||
|
<input type="text" class="layui-input" />
|
||||||
|
</div>
|
||||||
|
<div class="layui-btn-container">
|
||||||
|
<button
|
||||||
|
class="layui-btn layui-btn-primary layui-btn-sm"
|
||||||
|
colorpicker-events="clear"
|
||||||
|
>
|
||||||
|
清空
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="layui-btn layui-btn-sm"
|
||||||
|
colorpicker-events="confirm"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
确定
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</teleport>
|
||||||
|
</template>
|
||||||
|
<style scoped lang="less"></style>
|
34
src/module/colorPicker/colorPicker.type.ts
Normal file
34
src/module/colorPicker/colorPicker.type.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
export interface ColorPickerProps {
|
||||||
|
/**
|
||||||
|
* 默认颜色,不管你是使用 hex、rgb 还是 rgba 的格式输入,最终会以指定的格式显示。
|
||||||
|
* v-model:color
|
||||||
|
*/
|
||||||
|
color: string
|
||||||
|
/**
|
||||||
|
* 颜色显示/输入格式,可选值: hex、rgb
|
||||||
|
* 若在 rgb 格式下开启了透明度,格式会自动变成 rgba。在没有输入颜色的前提下,组件会默认为 #000 也就是黑色。
|
||||||
|
* default: hex(即 16 进制色值)
|
||||||
|
*/
|
||||||
|
format: 'hex' | 'rgb'
|
||||||
|
/**
|
||||||
|
* 是否开启透明度,若不开启,则不会显示透明框。开启了透明度选项时,当你的默认颜色为 hex 或 rgb 格式,
|
||||||
|
* 组件会默认加上值为 1 的透明度。相同的,当你没有开启透明度,却以 rgba 格式设置默认颜色时,组件会默认没有透明度。
|
||||||
|
* 注意:该参数必须配合 rgba 颜色值使用
|
||||||
|
* default: false
|
||||||
|
*/
|
||||||
|
alpha: boolean
|
||||||
|
/**
|
||||||
|
* 预定义颜色是否开启
|
||||||
|
* default: false
|
||||||
|
*/
|
||||||
|
predefine: boolean
|
||||||
|
/**
|
||||||
|
* 预定义颜色,此参数需配合 predefine: true 使用。
|
||||||
|
* 此处列举一部分:['#ff4500','#1e90ff','rgba(255, 69, 0, 0.68)','rgb(255, 120, 0)']
|
||||||
|
*/
|
||||||
|
colors: string[]
|
||||||
|
/**
|
||||||
|
* 下拉框大小,可以选择:lg、sm、xs。
|
||||||
|
*/
|
||||||
|
size: 'lg' | 'sm' | 'xs'
|
||||||
|
}
|
113
src/module/colorPicker/colorUtil.ts
Normal file
113
src/module/colorPicker/colorUtil.ts
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
export interface RGB {
|
||||||
|
h: number
|
||||||
|
s: number
|
||||||
|
b: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// RGB转HSB
|
||||||
|
export function RGBToHSB(rgb: any) {
|
||||||
|
const hsb = { h: 0, s: 0, b: 0 }
|
||||||
|
const min = Math.min(rgb.r, rgb.g, rgb.b)
|
||||||
|
const max = Math.max(rgb.r, rgb.g, rgb.b)
|
||||||
|
const delta = max - min
|
||||||
|
hsb.b = max
|
||||||
|
hsb.s = max != 0 ? (255 * delta) / max : 0
|
||||||
|
if (hsb.s != 0) {
|
||||||
|
if (rgb.r == max) {
|
||||||
|
hsb.h = (rgb.g - rgb.b) / delta
|
||||||
|
} else if (rgb.g == max) {
|
||||||
|
hsb.h = 2 + (rgb.b - rgb.r) / delta
|
||||||
|
} else {
|
||||||
|
hsb.h = 4 + (rgb.r - rgb.g) / delta
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hsb.h = -1
|
||||||
|
}
|
||||||
|
if (max == min) {
|
||||||
|
hsb.h = 0
|
||||||
|
}
|
||||||
|
hsb.h *= 60
|
||||||
|
if (hsb.h < 0) {
|
||||||
|
hsb.h += 360
|
||||||
|
}
|
||||||
|
hsb.s *= 100 / 255
|
||||||
|
hsb.b *= 100 / 255
|
||||||
|
return hsb
|
||||||
|
}
|
||||||
|
|
||||||
|
// HEX转HSB
|
||||||
|
export function HEXToHSB(hex: any) {
|
||||||
|
hex = hex.indexOf('#') > -1 ? hex.substring(1) : hex
|
||||||
|
if (hex.length == 3) {
|
||||||
|
const num = hex.split('')
|
||||||
|
hex = num[0] + num[0] + num[1] + num[1] + num[2] + num[2]
|
||||||
|
}
|
||||||
|
hex = parseInt(hex, 16)
|
||||||
|
const rgb = { r: hex >> 16, g: (hex & 0x00ff00) >> 8, b: hex & 0x0000ff }
|
||||||
|
return RGBToHSB(rgb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HSB转RGB
|
||||||
|
export function HSBToRGB(hsb: any) {
|
||||||
|
const rgb: any = {}
|
||||||
|
let h = hsb.h
|
||||||
|
const s = (hsb.s * 255) / 100
|
||||||
|
const b = (hsb.b * 255) / 100
|
||||||
|
if (s == 0) {
|
||||||
|
rgb.r = rgb.g = rgb.b = b
|
||||||
|
} else {
|
||||||
|
const t1 = b
|
||||||
|
const t2 = ((255 - s) * b) / 255
|
||||||
|
const t3 = ((t1 - t2) * (h % 60)) / 60
|
||||||
|
if (h == 360) h = 0
|
||||||
|
if (h < 60) {
|
||||||
|
rgb.r = t1
|
||||||
|
rgb.b = t2
|
||||||
|
rgb.g = t2 + t3
|
||||||
|
} else if (h < 120) {
|
||||||
|
rgb.g = t1
|
||||||
|
rgb.b = t2
|
||||||
|
rgb.r = t1 - t3
|
||||||
|
} else if (h < 180) {
|
||||||
|
rgb.g = t1
|
||||||
|
rgb.r = t2
|
||||||
|
rgb.b = t2 + t3
|
||||||
|
} else if (h < 240) {
|
||||||
|
rgb.b = t1
|
||||||
|
rgb.r = t2
|
||||||
|
rgb.g = t1 - t3
|
||||||
|
} else if (h < 300) {
|
||||||
|
rgb.b = t1
|
||||||
|
rgb.g = t2
|
||||||
|
rgb.r = t2 + t3
|
||||||
|
} else if (h < 360) {
|
||||||
|
rgb.r = t1
|
||||||
|
rgb.g = t2
|
||||||
|
rgb.b = t1 - t3
|
||||||
|
} else {
|
||||||
|
rgb.r = 0
|
||||||
|
rgb.g = 0
|
||||||
|
rgb.b = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { r: Math.round(rgb.r), g: Math.round(rgb.g), b: Math.round(rgb.b) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// HSB转HEX
|
||||||
|
export function HSBToHEX(hsb: any) {
|
||||||
|
const rgb = HSBToRGB(hsb)
|
||||||
|
const hex = [rgb.r.toString(16), rgb.g.toString(16), rgb.b.toString(16)]
|
||||||
|
hex.forEach((val, nr) => {
|
||||||
|
if (val.length == 1) {
|
||||||
|
hex[nr] = '0' + val
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return hex.join('')
|
||||||
|
}
|
||||||
|
|
||||||
|
//转化成所需rgb格式
|
||||||
|
export function RGBSTo(rgbs: any) {
|
||||||
|
const regexp = /[0-9]{1,3}/g
|
||||||
|
const re = rgbs.match(regexp) || []
|
||||||
|
return { r: re[0], g: re[1], b: re[2] }
|
||||||
|
}
|
@ -4,10 +4,55 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup></script>
|
<script lang="ts" setup>
|
||||||
|
import { Nullable } from '/@src/module/type'
|
||||||
|
import ColorBox from './ColorBox.vue'
|
||||||
|
|
||||||
|
interface ColorPickerProps {
|
||||||
|
color?: string
|
||||||
|
size?: Nullable<string>
|
||||||
|
alpha?: boolean
|
||||||
|
format?: 'hex' | 'rgb'
|
||||||
|
predefine?: boolean
|
||||||
|
colors?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const colorPickerProps = withDefaults(defineProps<ColorPickerProps>(), {
|
||||||
|
color: '',
|
||||||
|
size: () => null,
|
||||||
|
alpha: false,
|
||||||
|
format: 'hex',
|
||||||
|
predefine: false,
|
||||||
|
colors: () => [
|
||||||
|
//默认预定义颜色列表
|
||||||
|
'#009688',
|
||||||
|
'#5FB878',
|
||||||
|
'#1E9FFF',
|
||||||
|
'#FF5722',
|
||||||
|
'#FFB800',
|
||||||
|
'#01AAED',
|
||||||
|
'#999',
|
||||||
|
'#c00',
|
||||||
|
'#ff8c00',
|
||||||
|
'#ffd700',
|
||||||
|
'#90ee90',
|
||||||
|
'#00ced1',
|
||||||
|
'#1e90ff',
|
||||||
|
'#c71585',
|
||||||
|
'rgb(0, 186, 189)',
|
||||||
|
'rgb(255, 120, 0)',
|
||||||
|
'rgb(250, 212, 0)',
|
||||||
|
'#393D49',
|
||||||
|
'rgba(0,0,0,.5)',
|
||||||
|
'rgba(255, 69, 0, 0.68)',
|
||||||
|
'rgba(144, 240, 144, 0.5)',
|
||||||
|
'rgba(31, 147, 255, 0.73)',
|
||||||
|
],
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<pre>
|
<div class="layui-inline'">
|
||||||
// todo =_=
|
<ColorBox></ColorBox>
|
||||||
</pre>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
90
src/use/usePosition.ts
Normal file
90
src/use/usePosition.ts
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
// 是否滚动
|
||||||
|
export const hasScrollbar = () =>
|
||||||
|
document.body.scrollHeight >
|
||||||
|
(window.innerHeight || document.documentElement.clientHeight)
|
||||||
|
// 窗口宽高
|
||||||
|
export const winArea = (type?: any) =>
|
||||||
|
document.documentElement[type ? 'clientWidth' : 'clientHeight']
|
||||||
|
//
|
||||||
|
export const scrollArea: any = (type: any) => {
|
||||||
|
type = type ? 'scrollLeft' : 'scrollTop'
|
||||||
|
return document.body[type] | document.documentElement[type]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function usePosition(elem?: any, elemView?: any, obj?: any) {
|
||||||
|
if (!elemView) return
|
||||||
|
obj = obj || {}
|
||||||
|
|
||||||
|
//如果绑定的是 document 或 body 元素,则直接获取鼠标坐标
|
||||||
|
if (elem === document || elem.name === 'body') {
|
||||||
|
obj.clickType = 'right'
|
||||||
|
}
|
||||||
|
|
||||||
|
//绑定绑定元素的坐标
|
||||||
|
const rect =
|
||||||
|
obj.clickType === 'right'
|
||||||
|
? (function () {
|
||||||
|
const e = obj.e || window.event || {}
|
||||||
|
return {
|
||||||
|
left: e.clientX,
|
||||||
|
top: e.clientY,
|
||||||
|
right: e.clientX,
|
||||||
|
bottom: e.clientY,
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
: elem.getBoundingClientRect()
|
||||||
|
const elemWidth = elemView.offsetWidth //控件的宽度
|
||||||
|
const elemHeight = elemView.offsetHeight //控件的高度
|
||||||
|
|
||||||
|
const margin = 5
|
||||||
|
let left = rect.left
|
||||||
|
let top = rect.bottom
|
||||||
|
|
||||||
|
//相对元素居中
|
||||||
|
if (obj.align === 'center') {
|
||||||
|
left = left - (elemWidth - elem.offsetWidth) / 2
|
||||||
|
} else if (obj.align === 'right') {
|
||||||
|
left = left - elemWidth + elem.offsetWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
//判断右侧是否超出边界
|
||||||
|
if (left + elemWidth + margin > winArea('width')) {
|
||||||
|
left = winArea('width') - elemWidth - margin //如果超出右侧,则将面板向右靠齐
|
||||||
|
}
|
||||||
|
//左侧是否超出边界
|
||||||
|
if (left < margin) left = margin
|
||||||
|
|
||||||
|
//判断底部和顶部是否超出边界
|
||||||
|
if (top + elemHeight + margin > winArea()) {
|
||||||
|
//优先顶部是否有足够区域显示完全
|
||||||
|
if (rect.top > elemHeight + margin) {
|
||||||
|
top = rect.top - elemHeight - margin * 2 //顶部有足够的区域显示
|
||||||
|
} else {
|
||||||
|
//如果面板是鼠标右键弹出,且顶部没有足够区域显示,则将面板向底部靠齐
|
||||||
|
if (obj.clickType === 'right') {
|
||||||
|
top = winArea() - elemHeight - margin * 2
|
||||||
|
if (top < 0) top = 0 //不能溢出窗口顶部
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//定位类型
|
||||||
|
const position = obj.position
|
||||||
|
if (position) elemView.style.position = position
|
||||||
|
|
||||||
|
//设置坐标
|
||||||
|
elemView.style.left = left + (position === 'fixed' ? 0 : scrollArea(1)) + 'px'
|
||||||
|
elemView.style.top = top + (position === 'fixed' ? 0 : scrollArea()) + 'px'
|
||||||
|
|
||||||
|
//防止页面无滚动条时,又因为弹出面板而出现滚动条导致的坐标计算偏差
|
||||||
|
if (!hasScrollbar()) {
|
||||||
|
const rect1 = elemView.getBoundingClientRect()
|
||||||
|
//如果弹出面板的溢出窗口底部,则表示将出现滚动条,此时需要重新计算坐标
|
||||||
|
if (!obj.SYSTEM_RELOAD && rect1.bottom + margin > winArea()) {
|
||||||
|
obj.SYSTEM_RELOAD = true
|
||||||
|
setTimeout(function () {
|
||||||
|
usePosition(elem, elemView, obj)
|
||||||
|
}, 50)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user