基于vue的颜色选择器vue-color-picker

时间:2024-04-17 14:48:56

项目中有用到颜色选择器的童鞋们可以看过来了

关于color-picker的jquery的插件是有蛮多,不过vue组件没有吧,反正我没有找到,

虽然element-ui里面有这个,但是你愿意为了一个小功能去引入这么大的依赖库吗?

适用于:想写基于vue的单个color-picker功能的童鞋

甩github地址啦:https://github.com/alexzaolin/vue-color-picker

(麻烦觉得有用的童鞋帮我点个star,谢谢谢谢谢~~)

凑个字数贴一下代码(部分算法来自网络):

<template lang="html">
<div>
    <div class="color-picker-container" :id="containerId" :class="{\'active\':isShowPicker}">
        <div class="color"></div>
        <div class="wheel"></div>
        <div class="overlay"></div>
        <div class="h-marker marker"></div>
        <div class="sl-marker marker"></div>
    </div>
</div>
</template>

<script>
/**
 * author : alex 
 * email : 961163829@qq.com
 */
export default {
    props:{
        /*由父组件传递的默认颜色*/
        defaultColor:{
            type:String,
            default:"#000000"
        },
        /*目标元素,可以是input框或者按钮等*/
        targetElem:null
    },
    data(){
        return {
            isShowPicker:false,
            wheel:document.querySelector(\'.wheel\'),
            color:this.defaultColor,
            containerId:"color-picker-container",
            dom:{
                hMarker:null,
                slMarker:null,
                color:null,
                targetElem:null,
                container:null
            },
            radius:84,
            square:100,
            width:194
        }
    },
    mounted(){

        this.dom.container=document.querySelector(\'#\'+this.containerId);
        this.dom.hMarker=document.querySelector(\'.h-marker\');
        this.dom.slMarker=document.querySelector(\'.sl-marker\');
        this.dom.color=document.querySelector(\'.color\');
        this.dom.targetElem=document.querySelector(this.targetElem);


        this.init();
    },
    methods:{
        init:function(){
            var self = this;
            self.posInit();
            self.eventBind();

              // Init color
            self.setColor(self.color);
        },
        openPicker:function(){
            var self = this;
            this.isShowPicker = true;
            this.wheel=document.querySelector(\'.wheel\');
            /*色盘打开的时候绑定点击事件*/
            document.addEventListener("click",self.documentClick);
        },
        closePicker:function(){
            this.isShowPicker = false;
            /*色盘关闭的时候解绑事件*/
            document.removeEventListener("click",this.documentClick);
        },
        eventBind:function(){
            var self = this;
            self.dom.container.addEventListener("mousedown",self.mousedown);
        },
        documentClick:function(e){
            /*你可能不需要关闭色盘,那这个方法也是可以不需要的*/
            var parents1 = this.getParents(e,this.dom.container,true);;
            var parents2 = this.getParents(e,this.dom.targetElem,true);
            if(parents1.length===0&&parents2.length===0){
                this.closePicker();
            }
        },
        posInit:function(){
            /*色盘的位置计算*/
            var target = this.dom.targetElem;

            var top = this.getElementViewTop(target);
            var left = this.getElementViewLeft(target);

            this.dom.container.style.position = "fixed";
            this.dom.container.style.top = (top)+\'px\';
            
            this.dom.container.style.left = (left + this.dom.targetElem.offsetWidth)+\'px\';
            
        },
        getElementViewLeft:function(element){
            /*获取元素距离视窗左部距离*/
        var actualLeft = element.offsetLeft;
        var current = element.offsetParent;

        while (current !== null){
          actualLeft += current.offsetLeft;
          current = current.offsetParent;
        }

        if (document.compatMode == "BackCompat"){
          var elementScrollLeft=document.body.scrollLeft;
        } else {
          var elementScrollLeft=document.documentElement.scrollLeft; 
        }

        return actualLeft-elementScrollLeft;
      },
        getElementViewTop:function(element){
            /*获取元素距离视窗顶部距离*/
        var actualTop = element.offsetTop;
        var current = element.offsetParent;

        while (current !== null){
          actualTop += current. offsetTop;
          current = current.offsetParent;
        }

        if (document.compatMode == "BackCompat"){
          var elementScrollTop=document.body.scrollTop;
        } else {
          var elementScrollTop=document.documentElement.scrollTop; 
        }

        return actualTop-elementScrollTop;
      },
        getParents:function(e,parent,andSelf){
            /*获取祖先节点,返回一个数组*/
            var target = e.target;
            var parent = typeof parent===\'string\'?document.querySelector(parent):parent;

            var curTarget = target;

            var arr = typeof andSelf === "undefined"?[curTarget]:[];
            var result = [];
            
            while(true){
                if((typeof parent !== \'undefined\'&&curTarget == parent)||
                    typeof parent === \'undefined\'&&curTarget.nodeType===9){
                    arr.push(curTarget);
                    break;
                }
                if(!!curTarget){arr.push(curTarget)}

                if(!!curTarget.parentNode){
                    curTarget = curTarget.parentNode
                }else{
                    break;
                }
            }
            if(!!parent){
                return arr.indexOf(parent)>-1?arr:[];
            }else{
                return arr;
            }
        },
        getParent:function(e){
            /*获取父节点*/
            return e.target.nodeType!==9&&e.target.parentNode;
        },
        /**
         * 以下为组件内部函数
         * 算法来自网络
         */
        updateValue:function (event) {
            var self = this;

            var that = event.target;
            if (that.value && that.value != self.color) {
                self.setColor(that.value);
            }
          },
        /**
        * Change color with HTML syntax #123456
        */
        setColor:function (color) {
            var self = this;
            var unpack = self.unpack(color);
            if (self.color != color && unpack) {

                self.color = color;
                self.rgb = unpack;
                self.hsl = self.RGBToHSL(self.rgb);
                self.updateDisplay();
            }
        },
        /**
        * Change color with HSL triplet [0..1, 0..1, 0..1]
        */
        setHSL:function (hsl) {
            var self = this;
            self.hsl = hsl;
            self.rgb = self.HSLToRGB(hsl);
            self.color = self.pack(self.rgb);
            self.updateDisplay();
        },
        /**
        * Retrieve the coordinates of the given event relative to the center
        * of the widget.
        */
          widgetCoords:function (event) {
            var self = this;
            var x, y;
            var el = event.target || event.srcElement;
            var reference = self.wheel;
            
            if (typeof event.offsetX != \'undefined\') {
              // Use offset coordinates and find common offsetParent
              var pos = { x: event.offsetX, y: event.offsetY };

              // Send the coordinates upwards through the offsetParent chain.
              var e = el;
              while (e) {
                e.mouseX = pos.x;
                e.mouseY = pos.y;
                pos.x += e.offsetLeft;
                pos.y += e.offsetTop;
                e = e.offsetParent;
              }

              // Look for the coordinates starting from the wheel widget.
              var e = reference;
              var offset = { x: 0, y: 0 }
              while (e) {
                if (typeof e.mouseX != \'undefined\') {
                  x = e.mouseX - offset.x;
                  y = e.mouseY - offset.y;
                  break;
                }
                offset.x += e.offsetLeft;
                offset.y += e.offsetTop;
                e = e.offsetParent;
              }

              // Reset stored coordinates
              e = el;
              while (e) {
                e.mouseX = undefined;
                e.mouseY = undefined;
                e = e.offsetParent;
              }
            }
            else {
              // Use absolute coordinates
              var pos = self.absolutePosition(reference);
              x = (event.pageX || 0*(event.clientX + document.documentElement.scrollLeft)) - pos.x;
              y = (event.pageY || 0*(event.clientY + document.documentElement.scrollTop)) - pos.y;
            }
            // Subtract distance to middle
            return { x: x - self.width / 2, y: y - self.width / 2 };
        },
        /**
        * Mousedown handler
        */
        mousedown:function (event) {
            var self = this;
            if (!document.dragging) {
                document.documentElement.addEventListener(\'mousemove\', self.mousemove);
                document.documentElement.addEventListener(\'mouseup\', self.mouseup);
                document.dragging = true;
            }

            // Check which area is being dragged
            var pos = self.widgetCoords(event);
            self.circleDrag = Math.max(Math.abs(pos.x), Math.abs(pos.y)) * 2 > self.square;

            // Process
            self.mousemove(event);
            return false;
        },
        /**
        * Mousemove handler
        */
        mousemove:function (event) {
            var self = this;
        // Get coordinates relative to color picker center
            var pos = self.widgetCoords(event);

            // Set new HSL parameters
            if (self.circleDrag) {
            var hue = Math.atan2(pos.x, -pos.y) / 6.28;
            if (hue < 0) hue += 1;
                self.setHSL([hue, self.hsl[1], self.hsl[2]]);
            }
            else {
                var sat = Math.max(0, Math.min(1, -(pos.x / self.square) + .5));
                var lum = Math.max(0, Math.min(1, -(pos.y / self.square) + .5));
                self.setHSL([self.hsl[0], sat, lum]);
            }
            return false;
        },
        /**
        * Mouseup handler
        */
        mouseup:function () {
            var self = this;
            // Uncapture mouse
            document.documentElement.removeEventListener(\'mousemove\', self.mousemove);
            document.documentElement.removeEventListener(\'mouseup\', self.mouseup);
            document.dragging = false;
        },
        /**
        * Update the markers and styles
        */
        updateDisplay:function () {
            var self = this;
            // Markers
            var angle = self.hsl[0] * 6.28;
            
            self.dom.hMarker.style.left = Math.round(Math.sin(angle) * self.radius + self.width / 2) + \'px\';
            self.dom.hMarker.style.top = Math.round(-Math.cos(angle) * self.radius + self.width / 2) + \'px\';

            self.dom.slMarker.style.left = Math.round(self.square * (.5 - self.hsl[1]) + self.width / 2) + \'px\';
            self.dom.slMarker.style.top = Math.round(self.square * (.5 - self.hsl[2]) + self.width / 2) + \'px\';

            // Saturation/Luminance gradient
            self.dom.color.style.backgroundColor = self.pack(self.HSLToRGB([self.hsl[0], 1, 0.5]));


            // important
            // 将获取到的最新的color值emit出去
            self.$emit("onChange",self.color);
            //self.dom.targetElem.style.backgroundColor = self.color;
            //self.dom.targetElem.style.color = self.hsl[2] > 0.5 ? \'#000\' : \'#fff\';

            // Change linked value
            // if (self.dom.targetElem.value && self.dom.targetElem.value != self.color) {
            //     self.dom.targetElem.value = self.color;
            // }
        },
        /**
        * Get absolute position of element
        */
        absolutePosition:function (el) {
            var self = this;
            var r = { x: el.offsetLeft, y: el.offsetTop };
            // Resolve relative to offsetParent
            if (el.offsetParent) {
                var tmp = self.absolutePosition(el.offsetParent);
                r.x += tmp.x;
                r.y += tmp.y;
            }
            return r;
        },
        /* Various color utility functions */
        pack:function (rgb) {
            var r = Math.round(rgb[0] * 255);
            var g = Math.round(rgb[1] * 255);
            var b = Math.round(rgb[2] * 255);
            return \'#\' + (r < 16 ? \'0\' : \'\') + r.toString(16) +
                         (g < 16 ? \'0\' : \'\') + g.toString(16) +
                         (b < 16 ? \'0\' : \'\') + b.toString(16);
        },
        unpack:function (color) {
            if (color.length == 7) {
                return [parseInt(\'0x\' + color.substring(1, 3)) / 255,
                        parseInt(\'0x\' + color.substring(3, 5)) / 255,
                        parseInt(\'0x\' + color.substring(5, 7)) / 255];
            }
            else if (color.length == 4) {
                return [parseInt(\'0x\' + color.substring(1, 2)) / 15,
                        parseInt(\'0x\' + color.substring(2, 3)) / 15,
                        parseInt(\'0x\' + color.substring(3, 4)) / 15];
            }
        },
        HSLToRGB:function (hsl) {
            var m1, m2, r, g, b;
            var h = hsl[0], s = hsl[1], l = hsl[2];
            m2 = (l <= 0.5) ? l * (s + 1) : l + s - l*s;
            m1 = l * 2 - m2;
            return [this.hueToRGB(m1, m2, h+0.33333),
                    this.hueToRGB(m1, m2, h),
                    this.hueToRGB(m1, m2, h-0.33333)];
        },
        hueToRGB:function (m1, m2, h) {
            h = (h < 0) ? h + 1 : ((h > 1) ? h - 1 : h);
            if (h * 6 < 1) return m1 + (m2 - m1) * h * 6;
            if (h * 2 < 1) return m2;
            if (h * 3 < 2) return m1 + (m2 - m1) * (0.66666 - h) * 6;
            return m1;
        },
        RGBToHSL:function (rgb) {
            var min, max, delta, h, s, l;
            var r = rgb[0], g = rgb[1], b = rgb[2];
            min = Math.min(r, Math.min(g, b));
            max = Math.max(r, Math.max(g, b));
            delta = max - min;
            l = (min + max) / 2;
            s = 0;
            if (l > 0 && l < 1) {
                s = delta / (l < 0.5 ? (2 * l) : (2 - 2 * l));
            }
            h = 0;
            if (delta > 0) {
                if (max == r && max != g) h += (g - b) / delta;
                if (max == g && max != b) h += (2 + (b - r) / delta);
                if (max == b && max != r) h += (4 + (r - g) / delta);
                h /= 6;
            }
            return [h, s, l];
        }
    }
}
</script>

<style lang="css" scoped>
.color-picker-container {
    position: fixed;
    display: none;
  
}
.color-picker-container.active{
    display: block;
}
.color-picker-container * {
    position: absolute;
    cursor: crosshair;
}
.color-picker-container, .color-picker-container .wheel {
    width: 195px;
    height: 195px;
}
.color-picker-container .color, .color-picker-container .overlay {
    top: 47px;
    left: 47px;
    width: 101px;
    height: 101px;
}
.color-picker-container .wheel {
    width: 195px;
    height: 195px;
    background: url(./images/wheel.png) no-repeat;
    -webkit-background-size: 100%;
    background-size: 100%;
}
.color-picker-container .overlay {
    background: url(./images/mask.png) no-repeat;
    -webkit-background-size: 100%;
    background-size: 100%;
}
.color-picker-container .marker {
    width: 17px;
    height: 17px;
    margin: -8px 0 0 -8px;
    overflow: hidden;
    background: url(./images/marker.png) no-repeat;
    -webkit-background-size: 100%;
    background-size: 100%;
}
</style>