Commit 11b15a34 authored by zhengke's avatar zhengke

预览封面 logo替换

parent 862fcb1b
/**
* 颜色盒子类
*
* @param {Array} colorRange [[rMin, rMax],[gMin, gMax], [bMin, bMax]] 颜色范围
* @param {any} total 像素总数, imageData / 4
* @param {any} data 像素数据集合
*/
class ColorBox {
colorRange: unknown[];
total: number;
data: Uint8ClampedArray;
volume: number;
rank: number;
constructor(colorRange: any[], total: number, data: Uint8ClampedArray) {
this.colorRange = colorRange;
this.total = total;
this.data = data;
this.volume = (colorRange[0][1] - colorRange[0][0]) * (colorRange[1][1] - colorRange[1][0]) * (colorRange[2][1] - colorRange[2][0]);
this.rank = total * this.volume;
}
getColor() {
const total = this.total;
const data = this.data;
let redCount = 0,
greenCount = 0,
blueCount = 0;
for (let i = 0; i < total; i++) {
redCount += data[i * 4];
greenCount += data[i * 4 + 1];
blueCount += data[i * 4 + 2];
}
return [redCount / total, greenCount / total, blueCount / total];
}
}
// 获取切割边
const getCutSide = (colorRange: number[][]) => { // r:0,g:1,b:2
const arr = [];
for (let i = 0; i < 3; i++) {
arr.push(colorRange[i][1] - colorRange[i][0]);
}
return arr.indexOf(Math.max(arr[0], arr[1], arr[2]));
}
// 切割颜色范围
const cutRange = (colorRange: number[][], colorSide: number, cutValue: any) => {
const arr1: number[][] = [];
const arr2: number[][] = [];
colorRange.forEach(function (item) {
arr1.push(item.slice());
arr2.push(item.slice());
})
arr1[colorSide][1] = cutValue;
arr2[colorSide][0] = cutValue;
return [arr1, arr2];
}
// 找到出现次数为中位数的颜色
const __quickSort = (arr: any[]): any => {
if (arr.length <= 1) {
return arr;
}
const pivotIndex = Math.floor(arr.length / 2);
const pivot = arr.splice(pivotIndex, 1)[0];
const left = [];
const right = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i].count <= pivot.count) {
left.push(arr[i]);
}
else {
right.push(arr[i]);
}
}
return __quickSort(left).concat([pivot], __quickSort(right));
}
const getMedianColor = (colorCountMap: Record<string, number>, total: number) => {
const arr = [];
for (const key in colorCountMap) {
arr.push({
color: parseInt(key),
count: colorCountMap[key]
})
}
const sortArr = __quickSort(arr);
let medianCount = 0;
const medianIndex = Math.floor(sortArr.length / 2)
for (let i = 0; i <= medianIndex; i++) {
medianCount += sortArr[i].count;
}
return {
color: parseInt(sortArr[medianIndex].color),
count: medianCount
}
}
// 切割颜色盒子
const cutBox = (colorBox: { colorRange: number[][]; total: number; data: Uint8ClampedArray }) => {
const colorRange = colorBox.colorRange;
const cutSide = getCutSide(colorRange);
const colorCountMap: Record<string, number> = {};
const total = colorBox.total;
const data = colorBox.data;
// 统计出各个值的数量
for (let i = 0; i < total; i++) {
const color = data[i * 4 + cutSide];
if (colorCountMap[color]) {
colorCountMap[color] += 1;
}
else {
colorCountMap[color] = 1;
}
}
const medianColor = getMedianColor(colorCountMap, total);
const cutValue = medianColor.color;
const cutCount = medianColor.count;
const newRange = cutRange(colorRange, cutSide, cutValue);
const box1 = new ColorBox(newRange[0], cutCount, data.slice(0, cutCount * 4));
const box2 = new ColorBox(newRange[1], total - cutCount, data.slice(cutCount * 4));
return [box1, box2];
}
// 队列切割
const queueCut = (queue: any[], num: number) => {
while (queue.length < num) {
queue.sort((a: { rank: number }, b: { rank: number }) => {
return a.rank - b.rank
});
const colorBox = queue.pop();
const result = cutBox(colorBox);
queue = queue.concat(result);
}
return queue.slice(0, num)
}
// 颜色去重
const colorFilter = (colorArr: number[][], difference: number) => {
for (let i = 0; i < colorArr.length; i++) {
for (let j = i + 1; j < colorArr.length; j++) {
if (Math.abs(colorArr[i][0] - colorArr[j][0]) < difference && Math.abs(colorArr[i][1] - colorArr[j][1]) < difference && Math.abs(colorArr[i][2] - colorArr[j][2]) < difference) {
colorArr.splice(j, 1)
j--
}
}
}
return colorArr
}
/**
* 提取颜色
* @param colorNumber 提取最大颜色数量
* @param img 需要提取的图片
* @param difference 图片颜色筛选精准度
* @param callback 回调函数
*/
const themeColor = (colorNumber: number, img: CanvasImageSource, difference: number, callback: (arg0: number[][]) => void) => {
const canvas = document.createElement('canvas') as HTMLCanvasElement;
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
let width = 0
let height = 0
let imageData = null
canvas.width = img.width as number;
width = canvas.width as number
canvas.height = img.height as number
height = canvas.height
ctx.drawImage(img, 0, 0, width, height);
imageData = ctx.getImageData(0, 0, width, height).data;
const total = imageData.length / 4;
let rMin = 255,
rMax = 0,
gMin = 255,
gMax = 0,
bMin = 255,
bMax = 0;
// 获取范围
for (let i = 0; i < total; i++) {
const red = imageData[i * 4];
const green = imageData[i * 4 + 1];
const blue = imageData[i * 4 + 2];
if (red < rMin) {
rMin = red;
}
if (red > rMax) {
rMax = red;
}
if (green < gMin) {
gMin = green;
}
if (green > gMax) {
gMax = green;
}
if (blue < bMin) {
bMin = blue;
}
if (blue > bMax) {
bMax = blue;
}
}
const colorRange = [[rMin, rMax], [gMin, gMax], [bMin, bMax]];
const colorBox = new ColorBox(colorRange, total, imageData);
const colorBoxArr = queueCut([colorBox], colorNumber);
let colorArr = [];
for (let j = 0; j < colorBoxArr.length; j++) {
colorBoxArr[j].total && colorArr.push(colorBoxArr[j].getColor())
}
colorArr = colorFilter(colorArr, difference)
callback(colorArr);
}
export default themeColor
import { array } from '@amcharts/amcharts4/core'
import { padStart } from 'lodash'
/**
......@@ -184,4 +185,8 @@ export const DominantColour = () =>{
{ColorName:'黑',ColorValue:'#000000',Id:11},
{ColorName:'灰',ColorValue:'#BABABA',Id:12},
]
}
export const getRgbLevel = (colorarr:any) => {
return colorarr[0] * 0.299 + colorarr[1] * 0.587 + colorarr[2] * 0.114
}
\ No newline at end of file
......@@ -45,7 +45,7 @@
<div class="page-number">幻灯片 {{slideIndex + 1}} / {{slides.length}}</div>
</div>
<!-- <div style="position: fixed;left: 200px;top: 0;opacity: 1;">
<!-- <div style="position: fixed;left: 200px;top: -100px;opacity: 0;">
<canvas style="display: none" id="canvas"></canvas>
<div
id="extract-color-id"
......@@ -67,7 +67,7 @@ import useScreening from '@/hooks/useScreening'
import useLoadSlides from '@/hooks/useLoadSlides'
import { injectKeyDataSource, injectKeyTemplate } from '@/types/injectKey'
import ConfigService from '@/services/ConfigService'
import { getHtmlPlainText, query } from '@/utils/common'
import { getHtmlPlainText, query, getRgbLevel } from '@/utils/common'
import useEditor from '@/utils/Editor/index'
import { VIEWPORT_SIZE, VIEWPORT_VER_SIZE } from '@/configs/canvas'
......@@ -132,6 +132,9 @@ const acquiesceLogo = ref([
'https://im.oytour.com/pptist/static/logo6.png',
])
const tempDatas = ({} as any)
const colorList = ref([] as any)
watch(() => slideIndex.value, () => {
// 清除多选状态的幻灯片
if (selectedSlidesIndex.value.length) {
......@@ -422,6 +425,17 @@ const GetTripTemplate = async () =>{
else if(TempId.value) queryMsg.TempId = TempId.value
let dataRes = await ConfigService.GetTripTemplateSlide(queryMsg);
if (dataRes.data.resultCode == 1) {
// colorList.value = []
tempDatas.value = dataRes.data.data
// for(let i=0;i<tempDatas.value.PageImageList.length;i++){
// const img = new Image();
// img.src = `${tempDatas.value.PageImageList[i]}`;
// img.crossOrigin = 'anonymous';
// img.onload = () => {
// themeColor(1, img, 20, SetColor);
// };
// }
if(SalesBack.value==0||(SalesBack.value==1&&searchData.value.TempId)){
let viewportRatios = 0
if(dataRes.data.data.TemplateType!=2) {
......@@ -467,32 +481,8 @@ const GetTripTemplate = async () =>{
if(ConfigId.value>0){
newSlides = newSlides.filter((x:Slide)=>!x.isTripItems)
}
// 根据集团渲染logo
if(dataRes.data.data.AuthType<=1){
for(let i=0;i<newSlides.length;i++){
let x = newSlides[i]
let eles = x.elements.filter(y=>y.layerName && y.layerName.indexOf('logo')!=-1)
let newElementsImg = await ResolveTripLogoHandler(eles,i)
let newElements = []
x.elements.forEach(y=>{
if(newElementsImg?.elements){
newElementsImg?.elements.forEach(z=>{
if(y.id==z.id) y = JSON.parse(JSON.stringify(z))
})
}
newElements.push(y)
})
x.elements = newElements
}
}
layoutsStore.setLayouts(JSON.parse(JSON.stringify(newSlides)))
if(searchData.value.sellId&&!searchData.value.TempId) return
slidesStore.setSlides(newSlides)
CoverImgStore.setCoverImg(dataRes.data.data.CoverImg)
slidesStore.updateSlideIndex(0)
datas.DataSource.pageType = newSlides[0].pageType
if(SalesBack.value==0) slidesStore.setTitle(dataRes.data.data.Title)
getColorShade(newSlides)
}
queryObj.value.TempId = dataRes.data.data.TempId
......@@ -533,8 +523,43 @@ const generateUniqueId = () => {
return '-' + timestamp + '-' + randomNum
}
// 获取是否深色
const getColorShade = async (newSlides:any) => {
let dark = false
// 根据集团渲染logo
// if(tempDatas.value.AuthType<=1){
// for(let j=0;j<colorList.value.length;j++){
// if (getRgbLevel(colorList.value[j].Color) > 50) dark = true
// }
// for(let i=0;i<newSlides.length;i++){
// if(i>colorList.value.length-2) dark = false
// let x = newSlides[i]
// let eles = x.elements.filter(y=>y.layerName && y.layerName.indexOf('logo')!=-1)
// let newElementsImg = await ResolveTripLogoHandler(eles,i,dark)
// let newElements = []
// x.elements.forEach(y=>{
// if(newElementsImg?.elements){
// newElementsImg?.elements.forEach(z=>{
// if(y.id==z.id) y = JSON.parse(JSON.stringify(z))
// })
// }
// newElements.push(y)
// })
// x.elements = newElements
// }
// }
layoutsStore.setLayouts(JSON.parse(JSON.stringify(newSlides)))
if(searchData.value.sellId&&!searchData.value.TempId) return
slidesStore.setSlides(newSlides)
CoverImgStore.setCoverImg(tempDatas.value.CoverImg)
slidesStore.updateSlideIndex(0)
datas.DataSource.pageType = newSlides[0].pageType
if(SalesBack.value==0) slidesStore.setTitle(tempDatas.value.Title)
}
// 替换logo
const ResolveTripLogoHandler = async (items:any, slideIndex:number) =>{
const ResolveTripLogoHandler = async (items:any, slideIndex:number,dark:false) =>{
let elements = []
let tempNewSlide:any = null
let templateObj = JSON.parse(JSON.stringify(items))
......@@ -544,10 +569,10 @@ const ResolveTripLogoHandler = async (items:any, slideIndex:number) =>{
let y = templateObj[i]
let tempSize = await FileService.getImageSizeWithoutDownloading(templateObj[i].src)
let scale = parseInt(tempSize.width/tempSize.height)
if(Colors[1]||scale==6) templateObj[i].filters.invert = '20%'
if(scale==1) templateObj[i].src = acquiesceLogo.value[0]
if(scale<1) templateObj[i].src = acquiesceLogo.value[1]
if(scale>1) templateObj[i].src = acquiesceLogo.value[2]
// if(Colors[1]||scale==6) templateObj[i].filters.invert = '20%'
if(scale==1) templateObj[i].src = dark==true?acquiesceLogo.value[0]:acquiesceLogo.value[3]
if(scale<1) templateObj[i].src = dark==true?acquiesceLogo.value[1]:acquiesceLogo.value[4]
if(scale>1) templateObj[i].src = dark==true?acquiesceLogo.value[2]:acquiesceLogo.value[5]
if(tempNewSlide){
tempNewSlide.elements.push(...templateObj)
}else{
......@@ -593,7 +618,10 @@ const SetColor = (colorArr: number[][]) => {
colorBlock.style.cssText = 'height: 50px;width: 50px;margin-right: 10px;border-radius: 50%;';
colorBlock.style.backgroundColor = `rgb${bgc}`;
let list = [parseInt(colorArr[index][0]),parseInt(colorArr[index][1]),parseInt(colorArr[index][2])]
// colorList.value.push(list)
let obj = {
Color: list
}
colorList.value.push(obj)
extractColor.appendChild(colorBlock);
}
}
......
......@@ -20,6 +20,14 @@
</div>
</div>
</div>
<div style="position: fixed;left: 200px;top: -100px;opacity: 0;">
<canvas style="display: none" id="canvas"></canvas>
<div
id="extract-color-id"
class="extract-color"
style="display: flex;padding: 0 20px; justify-content:end;">
</div>
</div>
</template>
<script setup lang="ts">
......@@ -33,6 +41,8 @@ import { ref } from 'vue';
import { ElLoading } from 'element-plus'
import { useRouter } from 'vue-router'
import { useUserStore } from "@/store";
import themeColor from '@/utils/colorExtraction';
import { getRgbLevel } from '@/utils/common'
const router = useRouter()
const param = router.currentRoute.value.params
......@@ -49,6 +59,9 @@ const acquiesceLogo = ref([
'https://im.oytour.com/pptist/static/logo6.png',
])
const tempDatas = ({} as any)
const colorList = ([] as any)
const w = ref(0)
const h = ref(0)
......@@ -57,11 +70,22 @@ const loadSliders = async ()=>{
let response = await ConfigService.GetTripTemplateSlide({TempId:parseInt(param.tempId.toString())})
loadingObj.close()
if (response.data.resultCode == 1) {
let dataObj = response.data.data
w.value=dataObj.Width
h.value=dataObj.Height
colorList.value = []
tempDatas.value = dataObj
for(let i=0;i<tempDatas.value.PageImageList.length;i++){
const img = new Image();
img.src = `${tempDatas.value.PageImageList[i]}`;
img.crossOrigin = 'anonymous';
img.onload = () => {
themeColor(1, img, 20, SetColor);
};
}
if(w.value>0){
viewportRatio.value = h.value/w.value
if(viewportRatio.value>=1) VIEWPORT_VER_SIZE.Value=w.value
......@@ -74,33 +98,44 @@ const loadSliders = async ()=>{
SlidesData&&SlidesData.forEach((x:any,i:number)=>{
newSlides.push(x)
})
// 根据集团渲染logo
if(dataRes.data.data.AuthType<=1){
for(let i=0;i<newSlides.length;i++){
let x = newSlides[i]
let index =null
x.elements.forEach((y,indexs)=>{
if(y.layerName && y.layerName.indexOf('logo')!=-1) index = indexs
})
let eles = x.elements.filter(y=>y.layerName && y.layerName.indexOf('logo')!=-1)
let newElementsImg = await ResolveTripLogoHandler(eles,i)
let newElements = []
x.elements.forEach(y=>{
if(newElementsImg?.elements){
newElementsImg?.elements.forEach(z=>{
if(y.id==z.id) y = JSON.parse(JSON.stringify(z))
})
}
newElements.push(y)
setTimeout(()=>{
getColorShade(newSlides)
},500)
}
}
// 获取是否深色
const getColorShade = async (newSlides:any) => {
let dark = false
// 根据集团渲染logo
console.log(tempDatas.value.AuthType<=1,'-----------')
if(tempDatas.value.AuthType<=1){
for(let j=0;j<colorList.value.length;j++){
if (getRgbLevel(colorList.value[j].Color) > 50) dark = true
}
for(let i=0;i<newSlides.length;i++){
if(i>colorList.value.length-2) dark = false
let x = newSlides[i]
let eles = x.elements.filter(y=>y.layerName && y.layerName.indexOf('logo')!=-1)
let newElementsImg = await ResolveTripLogoHandler(eles,i,dark)
let newElements = []
x.elements.forEach(y=>{
if(newElementsImg?.elements){
newElementsImg?.elements.forEach(z=>{
if(y.id==z.id) y = JSON.parse(JSON.stringify(z))
})
x.elements = newElements
}
}
slidesStore.setSlides(newSlides)
newElements.push(y)
})
x.elements = newElements
}
}
slidesStore.setSlides(newSlides)
slidesStore.updateSlideIndex(0)
}
const ResolveTripLogoHandler = async (items:any, slideIndex:number) =>{
const ResolveTripLogoHandler = async (items:any, slideIndex:number,dark:false) =>{
let elements = []
let tempNewSlide:any = null
let templateObj = JSON.parse(JSON.stringify(items))
......@@ -110,10 +145,10 @@ const ResolveTripLogoHandler = async (items:any, slideIndex:number) =>{
let y = templateObj[i]
let tempSize = await FileService.getImageSizeWithoutDownloading(templateObj[i].src)
let scale = parseInt(tempSize.width/tempSize.height)
if(Colors[1]||scale==6) templateObj[i].filters.invert = '20%'
if(scale==1) templateObj[i].src = acquiesceLogo.value[0]
if(scale>1) templateObj[i].src = acquiesceLogo.value[2]
if(scale<1) templateObj[i].src = acquiesceLogo.value[1]
// if(Colors[1]||scale==6) templateObj[i].filters.invert = '20%'
if(scale==1) templateObj[i].src = dark==true?acquiesceLogo.value[0]:acquiesceLogo.value[3]
if(scale<1) templateObj[i].src = dark==true?acquiesceLogo.value[1]:acquiesceLogo.value[4]
if(scale>1) templateObj[i].src = dark==true?acquiesceLogo.value[2]:acquiesceLogo.value[5]
if(tempNewSlide){
tempNewSlide.elements.push(...templateObj)
}else{
......@@ -145,6 +180,27 @@ const ResolveTripLogoHandler = async (items:any, slideIndex:number) =>{
}
}
const SetColor = (colorArr: number[][]) => {
// 初始化删除多余子节点
const extractColor = document.querySelector('#extract-color-id') as HTMLElement;
while (extractColor.firstChild) {
extractColor.removeChild(extractColor.firstChild);
}
// 创建子节点
for (let index = 0; index < colorArr.length; index++) {
const bgc = '(' + colorArr[index][0] + ',' + colorArr[index][1] + ',' + colorArr[index][2] + ')';
const colorBlock = document.createElement('div') as HTMLElement;
colorBlock.id = `color-block-id${index}`;
colorBlock.style.cssText = 'height: 50px;width: 50px;margin-right: 10px;border-radius: 50%;';
colorBlock.style.backgroundColor = `rgb${bgc}`;
let list = [parseInt(colorArr[index][0]),parseInt(colorArr[index][1]),parseInt(colorArr[index][2])]
let obj = {
Color: list
}
colorList.value.push(obj)
extractColor.appendChild(colorBlock);
}
}
if(param.tempId){
loadSliders()
......
......@@ -139,12 +139,12 @@
<el-dropdown class="q-pl-lg" trigger="click">
<el-icon class="q-pt-sm" size="16" color="#b1b7cf"><MoreFilled /></el-icon>
<template #dropdown>
<el-dropdown-menu class="q-pa-md" @click.stop="OffEdit">
<el-dropdown-item>
<el-button type="default" link :icon="View" size="small" @click="getTemplate(scope.row)">详情</el-button>
<el-dropdown-menu class="q-pa-md">
<el-dropdown-item @click="getTemplate(scope.row)">
<el-button type="default" link :icon="View" size="small">详情</el-button>
</el-dropdown-item>
<el-dropdown-item>
<el-button type="default" link :icon="Delete" size="small" @click="deleteTemplate(scope.row)">删除</el-button>
<el-dropdown-item @click="deleteTemplate(scope.row)">
<el-button type="default" link :icon="Delete" size="small">删除</el-button>
</el-dropdown-item>
</el-dropdown-menu>
</template>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment