已关注
关注
重播 分享 赞


//线性混合(取平均值)const blendAverage =(colors)=> {const total = colors.reduce((acc, color) => {acc.r += color.r;acc.g += color.g;acc.b += color.b;return acc;}, { r: 0, g: 0, b: 0 }); //计算数组元素之和const count = colors.length;return {r: Math.round(total.r / count),g: Math.round(total.g / count),b: Math.round(total.b / count)}; //返回平均值}
2、颜色转换,十六进制转换为RGB/RGB转换为16进制如代码:// 工具函数,十六进制转RGBfunction hexToRgb(hex) {const bigint = parseInt(hex.slice(1), 16);return {r: (bigint >> 16) & 255,g: (bigint >> 8) & 255,b: bigint & 255};}// 工具函数,RGB转十六进制function rgbToHex(r, g, b) {return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase();}
<template><viewclass="color-blender-box uni-column"><!--顶部结果--><viewclass="cbb-item cbb-top uni-column"v-if="blendResult.hex != ''"><labelclass="cbb-m-label">融合结果:</label><viewclass="cbb-it-item uni-row"><textclass="cbb-iti-res":style="setResultStyle">{{ blendResult.hex }}</text></view><viewclass="cbb-it-item uni-column"><labelclass="cbb-m-label">原始颜色:</label><viewclass="cbb-iti-info uni-column"><viewclass="cbb-iti-i-block uni-row"v-for="(color,index) in chooseColor":key="index"><textclass="cbb-iti-ib-color":style="'background-color:'+color+';'"></text><textclass="cbb-iti-ib-text">{{ '颜色'+(index+1) }}</text><textclass="cbb-iti-ib-text">{{ color }}</text><textclass="cbb-iti-ib-text">({{ colorHexToRgb(color) }})</text></view></view><labelclass="cbb-m-label">融合结果:</label><viewclass="cbb-iti-info uni-column"><viewclass="cbb-iti-i-block uni-row"><textclass="cbb-iti-ib-color":style="'background-color:'+blendResult.hex+';'"></text><textclass="cbb-iti-ib-text">颜色</text><textclass="cbb-iti-ib-text">{{ blendResult.hex }}</text><textclass="cbb-iti-ib-text">({{ colorHexToRgb(blendResult.hex) }})</text></view></view><labelclass="cbb-m-label">融合模式:</label><viewclass="cbb-iti-info uni-column"><viewclass="cbb-iti-i-block uni-column"><textclass="cbb-iti-ib-text">{{ currentMode.name }}</text><textclass="cbb-iti-ib-text">{{ currentMode.desc }}</text></view></view></view></view><!--中间内容--><viewclass="cbb-item cbb-mid uni-column"><labelclass="cbb-m-label">融合颜色:</label><viewclass="cbb-m-item uni-row"><viewclass="cbb-mi-colors uni-row"v-for="(color,index) in chooseColor":key="index"><viewclass="cbb-mic-block"><viewclass="cbb-micb-text" @tap="openColorPicker(index)":style="'background-color:'+color+';'">{{ color }}</view></view></view><ColorPickerref="colorsPicker":defaultColor="chooseColor[currentPicker]" @confirm="subColor" @cancel="cancel" /></view><labelclass="cbb-m-label">融合模式:</label><viewclass="cbb-m-item uni-row"><viewclass="cbb-mi-modeValue"v-for="(item,index) in modeArr":key="index"><text:class="['cbb-mim-text',item.id ==currentMode.id?'cbb-mim-ac':'cbb-mim-nor']"@tap="changeMode(item)">{{ item.name }}</text></view></view><viewclass="cbb-m-tips"> {{ currentMode.desc }} </view></view><!--底部--><viewclass="cbb-item cbb-bottom uni-column"><viewclass="cbb-b-block uni-row"><textclass="cbb-bb-btn cbb-bb-reset" @tap="resetColors">重置</text><textclass="cbb-bb-btn cbb-bb-done" @tap="blendColors">颜色融合</text></view></view></view></template><scriptsetup>import ColorPicker from "@/components/picker/ColorPicker.vue"import {onMounted,ref,nextTick,computed} from "vue";//当前选中的模式const currentMode = ref({id: 1,name: '线性混合',desc: '线性混合: 所有颜色的RGB值取平均值'}); //默认为线性混合//融合模式const modeArr = ref([{id: 1,name: '线性混合',desc: '线性混合: 所有颜色的RGB值取平均值'},{id: 2,name: '正片叠底',desc: '正片叠底: 模拟颜料混合效果,使颜色变暗'},{id: 3,name: '滤色',desc: '滤色: 与正片叠底相反,使颜色变亮'},{id: 4,name: '叠加',desc: '叠加: 结合乘法和屏幕模式,保持高光和阴影'},{id: 5,name: '变暗',desc: '变暗: 比较所有颜色,选择最暗的值'},{id: 6,name: '变亮',desc: '变亮: 比较所有颜色,选择最亮的值'},]);//选择的颜色const chooseColor = ref(['#FF0000', '#00FF00', '#0000FF']); //选择的颜色 3种 默认'#FF0000','#00FF00','#0000FF'const currentPicker = ref(0); //当前选中的选择器 默认第一个const colorsPicker = ref(null); //组件const blendResult = ref({rgb:'',hex:''});//融合的结果onMounted(() => {});//打开颜色选择器const openColorPicker = (index) => {currentPicker.value = index;nextTick(() => {colorsPicker.value.open();})};// 颜色确认回调const subColor = (e) => {const {hex,rgba} = e;chooseColor.value.splice(currentPicker.value,1,hex);//更新选中的颜色值};const cancel = () => {console.log('取消');};//选中融合模式const changeMode = (obj) => {currentMode.value = obj;};//设置结果的样式const setResultStyle = computed(() => {const color = blendResult.value.hex;let textColor = '#FFFFFF';let border = "0rpx";if(color == "#FFFFFF"){textColor = "#000000";border = "1rpx#ddddddsolid";}return {backgroundColor:color,color:textColor,border: border};});//融合颜色const blendColors =()=>{const colors = chooseColor.value.map(item => hexToRgb(item)); //颜色转换为 RGBlet blended;switch(currentMode.value.id){case 2:blended=blendMultiply(colors); //正片叠底break;case 3:blended=blendScreen(colors); //滤色break;case 4:blended=blendOverlay(colors); //叠加break;case 5:blended=blendDarken(colors); //变暗break;case 6:blended=blendLighten(colors); //变亮break;default:blended = blendAverage(colors); //线性混合}const hex = rgbToHex(blended.r, blended.g, blended.b);console.log(hex,"融合得出的颜色")blendResult.value.hex = hex;blendResult.value.rgb = blended;}// 工具函数,RGB转十六进制function rgbToHex(r, g, b) {return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase();}// 工具函数,十六进制转RGBfunction hexToRgb(hex) {const bigint = parseInt(hex.slice(1), 16);return {r: (bigint >> 16) & 255,g: (bigint >> 8) & 255,b: bigint & 255};}//线性混合(平均值)const blendAverage =(colors)=> {const total = colors.reduce((acc, color) => {acc.r += color.r;acc.g += color.g;acc.b += color.b;return acc;}, { r: 0, g: 0, b: 0 });const count = colors.length;return {r: Math.round(total.r / count),g: Math.round(total.g / count),b: Math.round(total.b / count)};}//正片叠底const blendMultiply =(colors)=> {if (colors.length === 0) return { r: 0, g: 0, b: 0 };let result = { ...colors[0] };for (let i = 1; i < colors.length; i++) {result.r = Math.round((result.r * colors[i].r) / 255);result.g = Math.round((result.g * colors[i].g) / 255);result.b = Math.round((result.b * colors[i].b) / 255);}return result;}//滤色const blendScreen =(colors)=> {if (colors.length === 0) return { r: 255, g: 255, b: 255 };// 从第一个颜色开始let result = { ...colors[0] };// 对每个后续颜色应用滤色混合公式for (let i = 1; i < colors.length; i++) {// 滤色公式: 255 - ((255 - base) * (255 - blend)) / 255result.r = 255 - Math.round(((255 - result.r) * (255 - colors[i].r)) / 255);result.g = 255 - Math.round(((255 - result.g) * (255 - colors[i].g)) / 255);result.b = 255 - Math.round(((255 - result.b) * (255 - colors[i].b)) / 255);}return result;}//叠加const blendOverlay =(colors)=> {if (colors.length === 0) return { r: 128, g: 128, b: 128 };let result = { ...colors[0] };for (let i = 1; i < colors.length; i++) {result.r = result.r < 128? Math.round((2 * result.r * colors[i].r) / 255): 255 - Math.round((2 * (255 - result.r) * (255 - colors[i].r)) / 255);result.g = result.g < 128? Math.round((2 * result.g * colors[i].g) / 255): 255 - Math.round((2 * (255 - result.g) * (255 - colors[i].g)) / 255);result.b = result.b < 128? Math.round((2 * result.b * colors[i].b) / 255): 255 - Math.round((2 * (255 - result.b) * (255 - colors[i].b)) / 255);}return result;}//变暗const blendDarken =(colors)=> {if (colors.length === 0) return { r: 255, g: 255, b: 255 };let result = { ...colors[0] };for (let i = 1; i < colors.length; i++) {result.r = Math.min(result.r, colors[i].r);result.g = Math.min(result.g, colors[i].g);result.b = Math.min(result.b, colors[i].b);}return result;}//变亮const blendLighten =(colors)=> {if (colors.length === 0) return { r: 0, g: 0, b: 0 };let result = { ...colors[0] };for (let i = 1; i < colors.length; i++) {result.r = Math.max(result.r, colors[i].r);result.g = Math.max(result.g, colors[i].g);result.b = Math.max(result.b, colors[i].b);}return result;}//选中的颜色转换function colorHexToRgb(hex) {const bigint = parseInt(hex.slice(1), 16);const r = (bigint >> 16) & 255;const g = (bigint >> 8) & 255;const b = bigint & 255;const res = "R:"+r+", G:"+g+", B:"+b;return res;}//重置const resetColors =()=>{blendResult.value = {rgb:'',hex:''};chooseColor.value = ['#FF0000', '#00FF00', '#0000FF'];currentPicker.value = 0;currentMode.value = {id: 1,name: '线性混合',desc: '线性混合: 所有颜色的RGB值取平均值'};}</script><stylelang="scss">.color-blender-box {width: 100vw;height: 100vh;background-color:#f1f1f1;overflow: hidden;align-items: center;}.cbb-item {width: 90%;background-color:#ffffff;border-radius: 10rpx;padding: 10rpx;overflow: hidden;margin-top: 20rpx;}.cbb-top {flex: 2;justify-content: center;.cbb-it-item{flex-grow: 1;padding: 10rpx;.cbb-iti-res{width:100%;height: 150rpx;padding: 10rpx 0rpx;text-align: center;line-height: 150rpx;font-size: 1.2rem;font-weight: bold;border-radius: 10rpx;}}}.cbb-mid {.cbb-m-item {padding: 10rpx;align-items: center;flex-wrap: wrap;.cbb-mi-colors {padding: 10rpx;.cbb-mic-block {color:#ffffff;.cbb-micb-text {padding: 10rpx 20rpx;border-radius: 10rpx;}}}.cbb-mi-modeValue {padding: 10rpx;margin-top: 20rpx;.cbb-mim-text {padding: 10rpx 20rpx;background-color:#e0e7ff;border-radius: 10rpx;color:#4f46e5;}.cbb-mim-nor {border:#ffffff4rpx solid;}.cbb-mim-ac {border:#4f46e54rpx solid;}}}.cbb-m-label {font-size: 1rem;font-weight: bold;color:#333;padding-left: 20rpx;}.cbb-m-tips {padding: 10rpx;font-size: 0.9rem;color:#7c3aed;}}.cbb-bottom {height: 100rpx;margin-bottom: 20rpx;.cbb-b-block {padding: 10rpx;align-items: center;justify-content: center;.cbb-bb-btn {color: white;padding: 15rpx 20rpx;border-radius: 10rpx;font-size: 1.1rem;}.cbb-bb-reset {background-color:#d1d5db;margin-right: 50rpx;padding: 15rpx 40rpx;}.cbb-bb-done {background: linear-gradient(to right,#4f46e5,#7c3aed);}}}.cbb-iti-info{padding: 10rpx;.cbb-iti-i-block{align-items: center;.cbb-iti-ib-color{padding: 10rpx;height: 10rpx;width: 10rpx;}.cbb-iti-ib-text{padding:10rpx;font-size: 0.9rem;color:#333;}}}</style>
夜雨聆风