基于vue3 实现页面自动配色、颜色选择器组件

在网上找的各种颜色的相互转换函数,参考了elementUI的 color-picker组件

function FindRGB(q1, q2, hue) {
    if (hue > 360) hue = hue - 360;
    if (hue < 0) hue = hue + 360;
    if (hue < 60) return (q1 + (q2 - q1) * hue / 60);
    else if (hue < 180) return (q2);
    else if (hue < 240) return (q1 + (q2 - q1) * (240 - hue) / 60);
    else return (q1);
}

function HSLtoRGB(H, S, L) {
    var p1, p2;
    var tempRGB = { R: 0, G: 0, B: 0 }
    L /= 100;
    S /= 100;
    if (L <= 0.5) p2 = L * (1 + S);
    else p2 = L + S - (L * S);
    p1 = 2 * L - p2;
    if (S == 0) {
        tempRGB.R = L;
        tempRGB.G = L;
        tempRGB.B = L;
    }
    else {
        tempRGB.R = FindRGB(p1, p2, H + 120);
        tempRGB.G = FindRGB(p1, p2, H);
        tempRGB.B = FindRGB(p1, p2, H - 120);
    }
    tempRGB.R *= 255;
    tempRGB.G *= 255;
    tempRGB.B *= 255;
    tempRGB.R = Math.round(tempRGB.R);
    tempRGB.G = Math.round(tempRGB.G);
    tempRGB.B = Math.round(tempRGB.B);
    return tempRGB
}

function RGBtoHSL(r, g, b) {
    var Min = 0;
    var Max = 0;
    const HSL = { H: 0, S: 0, L: 0 }
    r = (eval(r) / 51) * .2;
    g = (eval(g) / 51) * .2;
    b = (eval(b) / 51) * .2;

    if (eval(r) >= eval(g))
        Max = eval(r);
    else
        Max = eval(g);
    if (eval(b) > eval(Max))
        Max = eval(b);

    if (eval(r) <= eval(g))
        Min = eval(r);
    else
        Min = eval(g);
    if (eval(b) < eval(Min))
        Min = eval(b);

    HSL.L = (eval(Max) + eval(Min)) / 2;
    if (eval(Max) == eval(Min)) {
        HSL.S = 0;
        HSL.H = 0;
    }
    else {
        if (HSL.L < .5)
            HSL.S = (eval(Max) - eval(Min)) / (eval(Max) + eval(Min));
        if (HSL.L >= .5)
            HSL.S = (eval(Max) - eval(Min)) / (2 - eval(Max) - eval(Min));
        if (r == Max)
            HSL.H = (eval(g) - eval(b)) / (eval(Max) - eval(Min));
        if (g == Max)
            HSL.H = 2 + ((eval(b) - eval(r)) / (eval(Max) - eval(Min)));
        if (b == Max)
            HSL.H = 4 + ((eval(r) - eval(g)) / (eval(Max) - eval(Min)));
    }
    HSL.H = Math.round(HSL.H * 60);
    if (HSL.H < 0) HSL.H += 360;
    if (HSL.H >= 360) HSL.H -= 360;
    HSL.S = Math.round(HSL.S * 100);
    HSL.L = Math.round(HSL.L * 100);
    return HSL
}
//计算辅助色
function getSecColor(r, g, b) {
    const { H, S, L } = RGBtoHSL(r, g, g)
    if (H > 120 && H < 290) {//cold
        if (Math.abs(210 - (H + 30)) < Math.abs(210 - (H - 30))) {
            return HSLtoRGB(H + 30, S, L)
        } else {
            return HSLtoRGB(H + 30, S, L)
        }

    } else {
        if (Math.abs(30 - (H + 30)) < Math.abs(30 - (H - 30))) {
            return HSLtoRGB(H + 30, S, L)
        } else {
            return HSLtoRGB(H + 30, S, L)
        }
    }
}

function getContrastColor(r, g, b) {
    const hsl = RGBtoHSL(r, g, b)
    hsl.H = (hsl.H + 180) % 360
    return HSLtoRGB(hsl.H, hsl.S, hsl.L)
}

function getLightenColor(r, g, b) {
    const { H, S, L } = RGBtoHSL(r, g, b)
    return HSLtoRGB(H, S, Math.min(100, L + 5))
}

function getDarkcolor(r, g, b) {
    const { H, S, L } = RGBtoHSL(r, g, b)
    return HSLtoRGB(H, S, Math.max(0, L - 5))
}
function setvar(key, value) {
    document.body.style.setProperty(key, value)
}
function rgb2hex(rgb) {
    rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
    function hex(x) {
        return ("0" + parseInt(x).toString(16)).slice(-2).toUpperCase();
    }
    return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
}
function rgba2hex(r, g, b, a) {
    function hex(x) {
        return ("0" + parseInt(x).toString(16)).slice(-2).toUpperCase();
    }
    return "#" + hex(r) + hex(g) + hex(b)+hex(256*a-1);
}

function hsv2hsl(hue, sat, val) {
    return [
        hue,
        (sat * val / ((hue = (2 - sat) * val) < 1 ? hue : 2 - hue)) || 0,
        hue / 2
    ];
};

// Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
// <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
const isOnePointZero = function (n) {
    return typeof n === 'string' && n.indexOf('.') !== -1 && parseFloat(n) === 1;
};

const isPercentage = function (n) {
    return typeof n === 'string' && n.indexOf('%') !== -1;
};

// Take input from [0, n] and return it as [0, 1]
const bound01 = function (value, max) {
    if (isOnePointZero(value)) value = '100%';

    const processPercent = isPercentage(value);
    value = Math.min(max, Math.max(0, parseFloat(value)));

    // Automatically convert percentage into number
    if (processPercent) {
        value = parseInt(value * max, 10) / 100;
    }

    // Handle floating point rounding errors
    if ((Math.abs(value - max) < 0.000001)) {
        return 1;
    }

    // Convert into [0, 1] range if it isn't already
    return (value % max) / parseFloat(max);
};

const INT_HEX_MAP = { 10: 'A', 11: 'B', 12: 'C', 13: 'D', 14: 'E', 15: 'F' };

const toHex = function ({ r, g, b }) {
    const hexOne = function (value) {
        value = Math.min(Math.round(value), 255);
        const high = Math.floor(value / 16);
        const low = value % 16;
        return '' + (INT_HEX_MAP[high] || high) + (INT_HEX_MAP[low] || low);
    };

    if (isNaN(r) || isNaN(g) || isNaN(b)) return '';

    return '#' + hexOne(r) + hexOne(g) + hexOne(b);
};

const HEX_INT_MAP = { A: 10, B: 11, C: 12, D: 13, E: 14, F: 15 };

const parseHexChannel = function (hex) {
    if (hex.length === 2) {
        return (HEX_INT_MAP[hex[0].toUpperCase()] || +hex[0]) * 16 + (HEX_INT_MAP[hex[1].toUpperCase()] || +hex[1]);
    }

    return HEX_INT_MAP[hex[1].toUpperCase()] || +hex[1];
};

const hsl2hsv = function (hue, sat, light) {
    sat = sat / 100;
    light = light / 100;
    let smin = sat;
    const lmin = Math.max(light, 0.01);
    let sv;
    let v;

    light *= 2;
    sat *= (light <= 1) ? light : 2 - light;
    smin *= lmin <= 1 ? lmin : 2 - lmin;
    v = (light + sat) / 2;
    sv = light === 0 ? (2 * smin) / (lmin + smin) : (2 * sat) / (light + sat);

    return {
        h: hue,
        s: sv * 100,
        v: v * 100
    };
};

// `rgbToHsv`
// Converts an RGB color value to HSV
// *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
// *Returns:* { h, s, v } in [0,1]
const rgb2hsv = function (r, g, b) {
    r = bound01(r, 255);
    g = bound01(g, 255);
    b = bound01(b, 255);

    const max = Math.max(r, g, b);
    const min = Math.min(r, g, b);
    let h, s;
    let v = max;

    const d = max - min;
    s = max === 0 ? 0 : d / max;

    if (max === min) {
        h = 0; // achromatic
    } else {
        switch (max) {
            case r:
                h = (g - b) / d + (g < b ? 6 : 0);
                break;
            case g:
                h = (b - r) / d + 2;
                break;
            case b:
                h = (r - g) / d + 4;
                break;
        }
        h /= 6;
    }

    return { h: h * 360, s: s * 100, v: v * 100 };
};

// `hsvToRgb`
// Converts an HSV color value to RGB.
// *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
// *Returns:* { r, g, b } in the set [0, 255]
const hsv2rgb = function (h, s, v) {
    h = bound01(h, 360) * 6;
    s = bound01(s, 100);
    v = bound01(v, 100);

    const i = Math.floor(h);
    const f = h - i;
    const p = v * (1 - s);
    const q = v * (1 - f * s);
    const t = v * (1 - (1 - f) * s);
    const mod = i % 6;
    const r = [v, q, p, p, t, v][mod];
    const g = [t, v, v, q, p, p][mod];
    const b = [p, p, t, v, v, q][mod];

    return {
        r: Math.round(r * 255),
        g: Math.round(g * 255),
        b: Math.round(b * 255)
    };
};

export default {
    FindRGB,
    HSLtoRGB,
    RGBtoHSL,
    getSecColor,
    getContrastColor,
    getLightenColor,
    getDarkcolor,
    setvar,
    rgb2hex,
    hsv2hsl,
    toHex,
    parseHexChannel,
    hsl2hsv,
    rgb2hsv,
    hsv2rgb,
    rgba2hex
}

组件的实现 w-theme.vue

<script setup>
import { computed, onMounted, reactive, ref, toRefs, useAttrs, watch } from 'vue';
import color from './color';
import Clipboard from 'clipboard'
const showBox = ref(false)
const HSV = reactive({ H: 0, S: 100, V: 50 })
const RGBA = reactive({ R: 0, G: 0, B: 0, A: 1 })

function fn_button_click() {
    showBox.value = !showBox.value
}

const hubBall = ref(null)
const opacityBall = ref(null)
const plateBall = ref(null)
function asyncBallPosition(r, g, b) {
    const hsv = color.rgb2hsv(r, g, b)
    setBallPosition(hubBall.value, hsv.h * 100 / 360 + "%")
    setBallPosition(opacityBall.value, "100%")
    setBallPosition(plateBall.value, hsv.s + "%", (100 - hsv.v) + "%")
}
function setHSV(r, g, b) {
    const hsv = color.rgb2hsv(r, g, b)
    HSV.H = hsv.h
    HSV.S = hsv.s
    HSV.V = hsv.v
}
function setBallPosition(ball, x, y) {
    const ballRect = ball.getBoundingClientRect()
    ball.style.left = `calc( ${x} - ${ballRect.width / 2}px )`
    if (y) {
        ball.style.top = `calc( ${y} - ${ballRect.height / 2}px )`
    }
}
function fn_handler(e, type) {
    // type  plate || hubBar || opacityBar
    const boxReact = e.target.getBoundingClientRect()
    const ball = e.target.children.item(0)
    const offsetX = e.clientX - boxReact.x, offsetY = e.clientY - boxReact.y

    let percentageX = offsetX / boxReact.width
    if (percentageX > 1) { percentageX = 1 }
    if (percentageX < 0) { percentageX = 0 }
    if (type == 'opacityBar') {//设置A
        RGBA.A = +percentageX.toFixed(2)
        setBallPosition(ball, percentageX * 100 + "%")
        return
    }
    if (type == 'hubBar') {//设置H
        HSV.H = +(percentageX * 360).toFixed(2)
        setBallPosition(ball, percentageX * 100 + "%")
    }
    if (type == "plate") {//设置 S、V
        HSV.S = +(percentageX * 100).toFixed(2)
        let percentageY = offsetY / boxReact.height
        if (percentageY > 1) { percentageY = 1 }
        if (percentageY < 0) { percentageY = 0 }
        HSV.V = +(100 - percentageY * 100).toFixed(2)
        setBallPosition(ball, percentageX * 100 + "%", percentageY * 100 + "%")
    }
    const rgbColor = color.hsv2rgb(HSV.H, HSV.S, HSV.V)
    RGBA.R = rgbColor.r
    RGBA.G = rgbColor.g
    RGBA.B = rgbColor.b
}

const hex = computed(() => {
    const opacity = parseInt(RGBA.A * 255).toString(16).toUpperCase().padStart(2, '0')
    return color.toHex({ r: RGBA.R, g: RGBA.G, b: RGBA.B }) + opacity
})
const hsla = computed(() => {
    const hsl = color.RGBtoHSL(RGBA.R, RGBA.G, RGBA.B)
    const alpha = +(RGBA.A * 100).toFixed(2)
    return `hsla(${hsl.H},${hsl.S}%,${hsl.L}%,${alpha}%)`
})
const exampleColors = computed(() =>
    ["#F44336", "#E91E63", "#9C27B0", "#673AB7", "#3F51B5", "#2196F3", "#03A9F4", "#00BCD4", "#00BCD4", "#4CAF50", "#8BC34A", "#CDDC39", "#FFEB3B", "#FFC107", "#FF9800", "#FF5722", "#795548", "#9E9E9E", "#607D8B", "#B0BEC5"]
)
watch(RGBA, () => {
    localStorage.setItem("sampleColor", hex.value)
    color.setvar('--main', hex.value)
    const dmain = color.getDarkcolor(RGBA.R, RGBA.G, RGBA.B)
    color.setvar('--dmain', color.rgba2hex(dmain.R, dmain.G, dmain.B, RGBA.A))
    const lmain = color.getLightenColor(RGBA.R, RGBA.G, RGBA.B)
    color.setvar('--lmain', color.rgba2hex(lmain.R, lmain.G, lmain.B, RGBA.A))
    const sec = color.getSecColor(dmain.R, dmain.G, dmain.B, RGBA.A)
    color.setvar('--sec', color.rgba2hex(sec.R, sec.G, sec.B, RGBA.A))
    const contrast = color.getContrastColor(RGBA.R, RGBA.G, RGBA.B)
    color.setvar('--contrast', color.rgba2hex(contrast.R, contrast.G, contrast.B, RGBA.A))
})



function fn_example_item_click(sample) {
    RGBA.B = ("0x" + sample.substr(-2)) / 1
    RGBA.G = ("0x" + sample.substr(-4, 2)) / 1
    RGBA.R = ("0x" + sample.substr(-6, 2)) / 1
    RGBA.A = 1
    asyncBallPosition(RGBA.R, RGBA.G, RGBA.B)
    setHSV(RGBA.R, RGBA.G, RGBA.B)

}

const button = ref(null)
const bx = ref(0), by = ref(0)
const box = ref(null)
const trangleDom = ref(null)
function setBoxPostion() {
    if (!showBox.value) {
        return
    }

    const buttonRect = button.value.getBoundingClientRect()
    const bodyRect = document.body.getBoundingClientRect()

    // width: 270px;
    // height: 370px;
    box.value.style.height = "370px"
    box.value.style.width = "270px"
    if (bodyRect.height - buttonRect.bottom > 375) {
        //按钮下方的空间可以容纳弹窗。
        by.value = buttonRect.bottom + 5;
        trangleDom.value.className = "trangle trangle-up"
    } else if (buttonRect.top > 375) {
        by.value = buttonRect.top - 375;
        trangleDom.value.className = "trangle trangle-down"
    } else {
        trangleDom.value.className = "trangle trangle-up"
        by.value = buttonRect.bottom + 5;
        box.value.style.height = bodyRect.height - buttonRect.bottom - 5 + "px"
        box.value.style.overflow = 'auto'
    }
    let left = buttonRect.left + buttonRect.width / 2 - 270 / 2
    if ((left + 270) > bodyRect.width) {
        left = bodyRect.width - 270 - 10
    } else if (left < 5) {
        left = 5
    }
    bx.value = left
    let trangleDomLeft = buttonRect.left - left + buttonRect.width / 2
    if (trangleDomLeft < 5) {
        trangleDomLeft = 5
    } else if (trangleDomLeft > 258) {
        trangleDomLeft = 258
    }
    trangleDom.value.style.left = trangleDomLeft + 'px'
}
watch(showBox, setBoxPostion)
function setClipboard() {
    new Clipboard('.res-item').on('success', e => {
        e.trigger.querySelector('.tips').innerHTML = "Copied"
        setTimeout(() => {
            e.trigger.querySelector('.tips').innerHTML = "COPY"
        }, 2000)
    })
}

const props = defineProps({
    color: {
        type: String,
        default: ''
    }
})
onMounted(() => {
    if (props.color) {
        fn_example_item_click(props.color.substring(0, 7))
    } else {
        const sampleColor = localStorage.getItem("sampleColor") || "#448aff"
        fn_example_item_click(sampleColor.substring(0, 7))
    }
    setClipboard()
    setBoxPostion()
    window.addEventListener('resize', setBoxPostion)
})


</script>
<template>
    <div class="w-theme-button" ref="button" @click="fn_button_click" v-bind="useAttrs()"></div>
    <div
        class="w-theme-box--mask"
        v-show="showBox"
        @scroll="showBox = false"
        @click="showBox = false"
    ></div>
    <transition name="fade">
        <div class="box" v-show="showBox" ref="box" :style="{ top: by + 'px', left: bx + 'px' }">
            <div
                class="plate"
                :style="`background-color:hsl(${HSV.H}deg,100%,50%)`"
                @mousedown="fn_handler($event, 'plate')"
            >
                <div class="ball" ref="plateBall"></div>
                <div class="bg bg1"></div>
                <div class="bg bg2"></div>
            </div>
            <div class="bar">
                <div class="target">
                    <div class="target-inner"></div>
                </div>
                <div class="bar-inner-right">
                    <div class="color-bar control-bar" @mousedown="fn_handler($event, 'hubBar')">
                        <div class="ball" ref="hubBall"></div>
                    </div>
                    <div
                        class="opacity-bar control-bar"
                        @mousedown="fn_handler($event, 'opacityBar')"
                    >
                        <div class="ball" ref="opacityBall"></div>
                        <div
                            class="opacity-inner-bar"
                            :style="`background-image:linear-gradient(to right,transparent,rgb(${RGBA.R},${RGBA.G},${RGBA.B}))`"
                        ></div>
                    </div>
                </div>
            </div>
            <div class="res">
                <div class="res-item" :data-clipboard-text="hex">
                    {{ hex }}
                    <div class="tips">COPY</div>
                </div>
                <div class="res-item" :data-clipboard-text="hsla">
                    {{ hsla }}
                    <div class="tips">COPY</div>
                </div>
            </div>
            <div class="example">
                <div
                    class="example-item"
                    v-for="color in exampleColors"
                    :key="color"
                    :style="{ background: color }"
                    @click="fn_example_item_click(color)"
                    :data-color="color"
                ></div>
            </div>
            <div class="example-panel">
                <div class="example-panel-item"></div>
                <div class="example-panel-item"></div>
                <div class="example-panel-item"></div>
                <div class="example-panel-item"></div>
                <div class="example-panel-item"></div>
            </div>
            <div class="trangle" ref="trangleDom"></div>
        </div>
    </transition>
</template>
<style lang="less">
@transpartBG: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==";
.border-radius {
    border-radius: 3px;
}
.fade-enter-active,
.fade-leave-active {
    transition: opacity 500ms ease;
}

.fade-enter-from,
.fade-leave-to {
    opacity: 0;
}
.w-theme-button {
    background-color: var(--main);
    width: 16px;
    height: 16px;
    border: 1px solid rgb(125, 26, 148);
    border-radius: 4px;
    display: inline-block;
    cursor: pointer;
}
.w-theme-box--mask {
    position: fixed;
    z-index: 9998;
    width: 100vw;
    height: 100vh;
    background-color: #62634f1f;
}
.box {
    position: fixed;
    // top: 20px;
    // left: 180px;
    width: 270px;
    height: 370px;
    background-color: #fff;
    box-shadow: 0px 0px 10px 4px #181b1e33;
    transition: opacity 200ms;
    user-select: none;
    z-index: 9999;
    .border-radius;
    .trangle {
        position: absolute;
        width: 0;
        height: 0;
        transform: translateX(-50%);
        &.trangle-up {
            bottom: 100%;
            border-bottom: 5px solid #fff;
            border-left: 5px solid transparent;
            border-right: 5px solid transparent;
        }
        &.trangle-down {
            top: 100%;
            border-top: 5px solid #fff;
            border-left: 5px solid transparent;
            border-right: 5px solid transparent;
        }
    }
    .example-panel {
        display: flex;
        height: 50px;
        justify-content: space-evenly;
        align-items: center;
        .example-panel-item {
            width: 15%;
            height: 60%;
            background-image: url(@transpartBG);
            border-radius: 4px;
            position: relative;
            &:after {
                content: "";
                position: absolute;
                display: inline-block;
                width: 100%;
                height: 100%;
                border-radius: 4px;
            }
            &:nth-child(1):after {
                background-color: var(--main);
            }
            &:nth-child(2):after {
                background-color: var(--lmain);
            }
            &:nth-child(3):after {
                background-color: var(--dmain);
            }
            &:nth-child(4):after {
                background-color: var(--sec);
            }
            &:nth-child(5):after {
                background-color: var(--contrast);
            }
        }
    }
    .example {
        display: flex;
        flex-wrap: wrap;
        padding-bottom: 10px;
        .border-radius;
        .example-item {
            width: 16px;
            height: 16px;
            border-radius: 4px;
            margin-top: 10px;
            margin-left: 10px;
            cursor: pointer;
            position: relative;
            &:hover {
                &:after {
                    content: attr(data-color);
                    position: absolute;
                    left: 10px;
                    background: #9e9e9ebf;
                    padding: 4px;
                    border-radius: 6px;
                    z-index: 1;
                    transform: translate(-50%, -50%);
                    bottom: calc(100% - 5px);
                    font-size: 12px;
                    color: white;
                }
                &::before {
                    content: "";
                    width: 0;
                    height: 0;
                    border-style: solid;
                    border-color: transparent;
                    border-left-width: 5px;
                    border-right-width: 5px;
                    border-top-color: #9e9e9ebf;
                    border-top-width: 5px;
                    border-bottom-width: 0;
                    display: inline-block;
                    position: absolute;
                    top: -5px;
                    z-index: -1;
                    transform: translateX(-50%);
                    left: 50%;
                }
            }
        }
    }
    .res {
        display: flex;
        height: 40px;
        border-bottom: 1px solid #eee;
        justify-content: space-evenly;
        .res-item {
            // flex: 1;
            line-height: 40px;
            text-align: center;
            font-size: 10px;
            padding-left: 10px;
            padding-right: 10px;
            position: relative;
            cursor: pointer;
            .tips {
                cursor: pointer;
                opacity: 0;
                position: absolute;
                top: 0;
                right: 0;
                // width: 40px;
                padding: 0 4px;
                height: 20px;
                background: #5e6cb9;
                transition: opacity 400ms;
                pointer-events: none;
                line-height: 20px;
                font-weight: bold;
                color: #b9ac5f;
            }
            &:hover {
                .tips {
                    opacity: 1;
                }
            }
        }
    }
    .plate {
        position: relative;
        height: 145px;
        .border-radius;
        .bg {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            box-sizing: border-box;
            pointer-events: none;
            &.bg1 {
                background-image: linear-gradient(to right, #fff, #ffffff00);
            }
            &.bg2 {
                background-image: linear-gradient(to bottom, transparent, #000);
            }
        }
        .ball {
            position: absolute;
            width: 12px;
            height: 12px;
            border-radius: 50%;
            border: 1px solid #eee;
            pointer-events: none;
            box-shadow: inset 0 0 1px 1px #1a2a3a73;
            z-index: 1;
        }
        .img {
            width: 100%;
            height: 100%;
            // background-color: var(--mainColor);
            pointer-events: none;
        }
    }
    .bar {
        height: 70px;
        background: #efefef;
        display: flex;
        justify-content: space-evenly;
        align-items: center;

        .target {
            width: 40px;
            height: 40px;
            // border: 1px solid #eeeeee33;
            box-shadow: 0 0 6px 1px #2e262657;
            border-radius: 50%;
            background-image: url(@transpartBG);
            overflow: hidden;
            .target-inner {
                background-color: var(--main);
                width: 100%;
                height: 100%;
            }
        }
        .bar-inner-right {
            width: 170px;
            justify-self: flex-end;
            align-self: stretch;
            display: flex;
            flex-direction: column;
            justify-content: space-evenly;

            .control-bar {
                position: relative;
                height: 15px;
                width: 100%;
                border-radius: 4px;
                &.color-bar {
                    background-image: linear-gradient(
                        to right,
                        #ff0000,
                        #ff3300,
                        #ff6600,
                        #ff9900,
                        #ffcc00,
                        #ffff00,
                        #ccff00,
                        #99ff00,
                        #66ff00,
                        #33ff00,
                        #00ff00,
                        #00ff33,
                        #00ff66,
                        #00ff99,
                        #00ffcc,
                        #00ffff,
                        #00ccff,
                        #0099ff,
                        #0066ff,
                        #0033ff,
                        #0000ff,
                        #3300ff,
                        #6600ff,
                        #9900ff,
                        #cc00ff,
                        #ff00ff,
                        #ff00cc,
                        #ff0099,
                        #ff0066,
                        #ff0033
                    );
                }
                &.opacity-bar {
                    background: url(@transpartBG);
                    .opacity-inner-bar {
                        pointer-events: none;
                        width: 100%;
                        height: 100%;
                        border-radius: 4px;
                    }
                    .ball {
                        right: -8px;
                    }
                }
                .ball {
                    position: absolute;
                    top: -1px;
                    width: 17px;
                    height: 17px;
                    background-color: #f8f8f8;
                    border-radius: 50%;
                    opacity: 0.9;
                    pointer-events: none;
                    cursor: pointer;
                    box-shadow: inset 0 0 1px 1px #1a2a3a73;
                }
            }
        }
    }
}
.sample {
    display: inline-flex;
    background: #fff;
    .block {
        width: 90px;
        height: 50px;
        border: 1px solid;
        margin: 6px;
    }
}
.hex-codes {
    display: flex;
    width: 14px * 18;
    flex-wrap: wrap;

    .inner {
        width: 14px;
        height: 14px;
        border-right: 1px solid;
        border-bottom: 1px solid;
        box-sizing: border-box;
        cursor: pointer;
        position: relative;
        transition: 200ms transform;
        &:hover {
            box-shadow: inset 0px 0px 2px 1px #000000b0;
            transform: scale(1.3);
            z-index: 1;
            &:after {
                content: attr(color);
                display: inline-block;
                position: absolute;
                background-color: #1a2a3a;
                padding: 6px;
                border-radius: 8px;
                z-index: 1;
                color: #fff;
                font-size: 12px;
                transform: translate(-50%);
                bottom: calc(100% + 5px);
                left: 50%;
            }
        }
    }
}
</style>

使用方法。


import wTheme from './components/w-theme.vue';


<w-theme class="theme" color="#3F51B5FF" />

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wgl758

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值