根据图片取其背景色

该博客介绍了如何在iview的Upload组件中,利用colorthief插件从上传的图片中提取主色,并将其设置为图片背景色。博主提供了四种方法,包括直接使用colorthief,以及三种自定义的图片取色算法。代码示例展示了从RGB到HSV的转换,以增强颜色饱和度和亮度,并最终输出16进制色值。此外,还展示了如何在父组件中监听颜色值的变化。
npm i --save colorthief //引入colorthief插件

在iview的Upload上传图片的控件中,当上传成功后,自动吸取图片的主色当做图片的背景色,总结了以下四种方法,第四种是利用colorthief插件,第一种,第二种,第三种是利用的图片取主色原理,至于从rgb转为hsv,在计算是为了提高取出的背景色的饱和度和明亮度,大家可酌情自己进行更改,我最后输出的色值是16进制的。
如图:
在这里插入图片描述

<template>
    <div class="wrap" :style="wrapStyle">
        <div class="jz bgfont" v-html="tips">
            {{ this.payload.width }} * {{ this.payload.height }}
        </div>
        <Upload
            class="uploader"
            ref="upload"
            :format="['jpg', 'jpeg', 'png', 'gif']"
            :max-size="4096"
            :on-success="handleSuccess"
            :on-error="handleError"
            :on-format-error="handleFormatError"
            :on-exceeded-size="handleMaxSize"
            :before-upload="handleBeforeUpload"
            with-credentials
            multiple
            :show-upload-list="false"
            action="/General/Upload/Picture/upload"
        >
            <div :style="wrapStyle" class="wrap bg">
                <div class="jz">
                    <Icon type="ios-cloud-upload" size="36"></Icon>
                    <p class="tips">点击或者拖拽上传</p>
                </div>
            </div>
        </Upload>
        <img
            v-show="!!imgUrl"
            :src="imgUrl"
            class="img"
            crossorigin="anonymous"
            @load="onload"
        />
        <div
            style="width: 600px; height: 300px"
            :style="{ background: color }"
            id="color"
        >
            <p style="margin-top: 80px">颜色</p>
        </div>
    </div>
</template>
<style scoped>
.img {
    width: 100%;
    height: 100%;
    position: absolute;
    z-index: 3;
    top: 0;
    left: 0;
}

.wrap {
    position: relative;
    margin: 0 auto;
    background: #fafafa;
}
#color {
    position: absolute;
    z-index: 1;
    top: -20px;
    left: -90px;
    color: white;
}
.uploader {
    position: absolute;
    z-index: 4;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    cursor: pointer;
}

.jz {
    position: absolute;
    width: 140px;
    height: auto;
    top: 50%;
    transform: translate(-50%, -50%);
    left: 50%;
    /*margin-left: -70px;*/
    text-align: center;
    color: #fff;
    text-shadow: 1px 1px #555;
    line-height: 1.4;
}

.bg {
    transition: 0.3s ease all;
    background: rgba(0, 0, 0, 0.3);
    opacity: 0;
}

.bg:hover {
    opacity: 1;
}

.bgfont {
    color: #ddd;
    text-shadow: none;
    font-size: 22px;
    line-height: 60px;
}

.tips {
    font-size: 12px;
}
</style>
<script>
import ColorThief from 'colorthief'
import { encode } from 'blurhash';
const encodeBlurHashOfImage = async (url) => {
    // 将图片 url 加载到 img 对象
    const loadImg = (url) => new Promise((rs) => {
        const img = new Image();
        img.onload = () => rs(img);
        img.src = url;
    });
    const img = await loadImg(url);
    // 计算合适的 blurhash 尺寸
    const blurSize = (img) => {
        let {
            naturalWidth: width,
            naturalHeight: height,
        } = img;
        const ratio = Math.max(width / 50, height / 50);
        return {
            width: parseInt(width / ratio),
            height: parseInt(height / ratio),
        };
    };
    const size = blurSize(img);
    // 创建画板
    const cvs = document.createElement('canvas');
    cvs.width = size.width;
    cvs.height = size.height;
    const ctx = cvs.getContext('2d');
    ctx.drawImage(img, 0, 0, size.width, size.height);
    // 计算 blurhash
    await new Promise(rs => setTimeout(rs, 0));
    const { data, width, height } = ctx.getImageData(0, 0, size.width, size.height);
    const hash = encode(data, width, height, 4, 4);
    return {
        hash,
        size: `${size.width}x${size.height}`,
    };
};

export default {
    data() {
        let type = Object.prototype.toString.call(this.payload),
            width,
            height,
            tips = '';
        if (type == '[object Object]') {
            width = this.payload.width * this.payload.scale + 'px';
            height = this.payload.height * this.payload.scale + 'px';
            tips = this.payload.width + ' * ' + this.payload.height;
        } else {
            width = this.payload[0].width * this.payload[0].scale + 'px';
            height = this.payload[0].height * this.payload[0].scale + 'px';
            this.payload.forEach((item) => {
                tips += item.width + ' * ' + item.height + '、';
            });
        }
        return {
            color: '',
            imgUrl: '',
            blurhashValue: '',
            wrapStyle: {
                width,
                height
            },
            tips,
        };
    },
    props: {
        blurhash: {
            type: Boolean,
            default: false,
        },
        value: {
            type: String,
            default: '',
        },
        payload: {
            type: [Object, Array],
            default: () => ({}),
        },
    },
    methods: {
        onload() {
            //第四种
            const img = document.querySelector('.img');
            const colorthief = new ColorThief();
            let colorImg = colorthief.getColor(img);
            let value = `rgb(${colorImg[0]},${colorImg[1]},${colorImg[2]})`
            console.log(value)
            let aa=this.showHsv(colorImg[0], colorImg[1], colorImg[2])
            console.log(aa)
            let bb=this.hsvShowRGB(aa[0],aa[1],aa[2])
            console.log(bb)
            this.color = this.showRGB(bb)
            this.$emit('colorValue', this.color)
            //  第一种,第二种,第三种
            //             setTimeout(() => {
            //                 let rgb = this.colorfulImg(img);
            //                 let value = `rgb(${rgb.r},${rgb.g},${rgb.b})`
            //                 this.color = this.showRGB(value)
            //                 this.$emit('colorValue', this.color)
            //                 console.log(this.color, 'pppp')
            //             }, 500)
        },
        colorfulImg: function (img) {
            const canvas = document.createElement('canvas');
            let length,
                i = -4,
                blockSize = 5,
                count = 0,
                rgb = { r: 0, g: 0, b: 0 }
            canvas.width = this.payload.width;
            canvas.height = this.payload.height;

            const context = canvas.getContext && canvas.getContext('2d');
            console.log(img)
            context.drawImage(img, 0, 0);

            // 获取像素数据
            let data = context.getImageData(0, 0, this.payload.width, this.payload.height).data;
            console.log(data)
            //第一种
            //length = data.length
            //while ((i += blockSize * 4) < length) {
            //    ++count;
            //    rgb.r += data[i];
            //    rgb.g += data[i + 1];
            //    rgb.b += data[i + 2];
            //}
            //rgb.r = ~~(rgb.r / count);
            //rgb.g = ~~(rgb.g / count);
            //rgb.b = ~~(rgb.b / count);
            //console.log(rgb)
            //第二种
            //  rgb={
            //     r:data[0],
            //     g:data[1],
            //     b:data[2]
            // }
            //第三种平均值算法(二维的实现)
            // rgb = { r: 1, g: 1, b: 1 }
            // // 取所有像素的平均值
            // for (let row = 0; row < this.payload.height; row++) {
            //     for (let col = 0; col < this.payload.width; col++) {
            //         // console.log(data[((img.width * row) + col) * 4])
            //         if (row == 0) {
            //             rgb.r += data[((this.payload.width * row) + col)];
            //             rgb.g += data[((this.payload.width * row) + col) + 1];
            //             rgb.b += data[((this.payload.width * row) + col) + 2];
            //         } else {
            //             rgb.r += data[((this.payload.width * row) + col) * 4];
            //             rgb.g += data[((this.payload.width * row) + col) * 4 + 1];
            //             rgb.b += data[((this.payload.width * row) + col) * 4 + 2];
            //         }
            //     }
            // }
            // // 求取平均值
            // rgb.r /= (this.payload.width * this.payload.height);
            // rgb.g /= (this.payload.width * this.payload.height);
            // rgb.b /= (this.payload.width * this.payload.height);

            // // 将最终的值取整
            // rgb.r = Math.round(rgb.r);
            // rgb.g = Math.round(rgb.g);
            // rgb.b = Math.round(rgb.b);
            return rgb;
        },
        //rgb转hsv
        showHsv(r, g, b) {
            r = r / 255;
            g = g / 255;
            b = b / 255;
            let h, s, v;
            let min = Math.min(r, g, b);
            let max = v = Math.max(r, g, b);
            let l = (min + max) / 2;
            let difference = max - min;

            if (max == min) {
                h = 0;
            } else {
                switch (max) {
                    case r: h = (g - b) / difference + (g < b ? 6 : 0); break;
                    case g: h = 2.0 + (b - r) / difference; break;
                    case b: h = 4.0 + (r - g) / difference; break;
                }
                h = Math.round(h * 60);
            }
            if (max == 0) {
                s = 0;
            } else {
                s = 1 - min / max;
            }
            s = Math.round(s * 100);
            v = Math.round(v * 100);
            console.log('原始',[h,s,v])
            let s1=30+0.5*s
            let v1=20+0.4*v
            return [h, s1, v1]
        },
        //hsv转rgb
        hsvShowRGB(h, s, v) {
            var s = s / 100;
            var v = v / 100;
            var h1 = Math.floor(h / 60) % 6;
            var f = h / 60 - h1;
            var p = v * (1 - s);
            var q = v * (1 - f * s);
            var t = v * (1 - (1 - f) * s);
            var r, g, b;
            switch (h1) {
                case 0:
                    r = v;
                    g = t;
                    b = p;
                    break;
                case 1:
                    r = q;
                    g = v;
                    b = p;
                    break;
                case 2:
                    r = p;
                    g = v;
                    b = t;
                    break;
                case 3:
                    r = p;
                    g = q;
                    b = v;
                    break;
                case 4:
                    r = t;
                    g = p;
                    b = v;
                    break;
                case 5:
                    r = v;
                    g = p;
                    b = q;
                    break;
            }
            return `rgb(${Math.ceil(r*255)},${Math.ceil(g*255)},${Math.ceil(b*255)})`
        },
        //rgb转16进制
        showRGB(str) {
            let hexcode = "#";
            let v = str.substring(4, str.length - 1);
            let s = v.split(",");
            for (let x = 0; x < 3; x++) {
                let n = s[x];
                if (n === "") n = "0";
                let c = "0123456789ABCDEF",
                    b = "",
                    a = n % 16;
                b = c.substr(a, 1);
                a = (n - a) / 16;
                hexcode += c.substr(a, 1) + b
            }
            return hexcode;
        },
        handleSuccess(res, file) {
            if (res.status === 2) {
                let type = Object.prototype.toString.call(this.payload);
                if (type == '[object Object]') {
                    if ((this.payload.width && (this.payload.width !== res.data.width)) || (this.payload.height && (this.payload.height !== res.data.height))) {
                        this.$Notice.warning({
                            title: '图片尺寸不符合要求',
                            desc: '标准尺寸为 ' + this.payload.width + ' * ' + this.payload.height
                        });
                    } else {
                        const { url } = res.data;
                        this.commitValue(url, res);
                    }
                } else if (type == '[object Array]') {
                    let pass = false,
                        desc = '';
                    this.payload.forEach((item) => {
                        if (item.width === res.data.width && item.height === res.data.height) {
                            this.$set(this.wrapStyle, 'width', (item.width * item.scale) + 'px');
                            this.$set(this.wrapStyle, 'height', (item.height * item.scale) + 'px');
                            pass = true;
                        }
                        desc += item.width + ' * ' + item.height + '、';
                    });
                    if (pass) {
                        const { url } = res.data;
                        this.commitValue(url, {
                            height: res.data.height,
                            width: res.data.width,
                        });
                    } else {
                        this.$Notice.warning({
                            title: '图片尺寸不符合要求',
                            desc: '标准尺寸为 ' + desc
                        });
                    }
                }
            } else {
                this.$Message.error('图片文件上传错误!')
            }
        },
        handleError(res, file) {
            this.$Message.error('图片文件上传错误!')
        },
        handleFormatError(file) {
            this.$Message.error('图片文件格式错误!')
            // 格式不对
        },
        handleMaxSize(file) {
            this.$Notice.warning({
                title: '超出文件大小',
                desc: '文件  ' + file.name + ' 太大了, 超出 2M 的文件不被允许。'
            });
        },
        handleBeforeUpload(file) {
            this.lastImageLocalUrl = URL.createObjectURL(file);
            return true
        },
        async commitValue(imgUrl, imgStyle) {
            let finalUrl = imgUrl;
            if (this.blurhash) {
                const { lastImageLocalUrl } = this;
                if (!lastImageLocalUrl) this.$Notice.warning('BlurHash 处理异常:未找到上传图片的本地链接');
                const { hash, size } = await encodeBlurHashOfImage(lastImageLocalUrl);

                const sp = imgUrl.indexOf('?') < 0 ? '?' : '&';
                const blurParams = `_blurhash=${encodeURIComponent(hash)}&_blursize=${size}`;
                finalUrl = finalUrl.concat(`${sp}${blurParams}`);
            }
            this.$emit('input', finalUrl);
            this.$emit('updateStyle', imgStyle);
        },
    },
    mounted: function () {

    },
    watch: {
        value: {
            handler(nv, ov) {
                this.imgUrl = nv;
            },
            deep: false,
            immediate: true
        },
    }
}
</script>

该子组件在父组件的引用

<template>
    <div class="page-comic-cartoon-contact">
        <getImage
            v-model="sub_cover_url"
            :payload="payloadicon"
            @colorValue="colorValue"
        ></getImage>
    </div>
</template>

<script>
import getImage from '~/components/getImage.vue';
export default {
    name: 'page-comic-cartoon-contact',
    components: { getImage },
    data() {
        return {
            sub_cover_url:'',
            payloadicon: {
                width: 416,//想传入的图片宽度
                height: 242,//想传入图片的高度
                scale:1//图片在页面展示缩放大小
            },
        };
    },
    methods: {
        colorValue(data){
            console.log(data)//可得输出的16进制的背景色值
        }
    },
    mounted(){
        this.colorValue()
    }
}
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值