From 3a2303dd40bb858c6430a35e6544de2529b3c483 Mon Sep 17 00:00:00 2001 From: LOG1997 <2694233102@qq.com> Date: Mon, 12 Jan 2026 22:46:25 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E2=9C=A8=20=E8=AE=BE=E7=BD=AE=E6=9C=80?= =?UTF-8?q?=E5=A4=9A=E6=8A=BD30=E4=BA=BA=E7=9A=84=E5=8D=A1=E7=89=87?= =?UTF-8?q?=E6=8E=92=E5=88=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useElement.ts | 100 ---------- src/hooks/useElement/index.ts | 323 +++++++++++++++++++++++++++++++++ src/views/Home/useViewModel.ts | 63 ++++++- 3 files changed, 381 insertions(+), 105 deletions(-) delete mode 100644 src/hooks/useElement.ts create mode 100644 src/hooks/useElement/index.ts diff --git a/src/hooks/useElement.ts b/src/hooks/useElement.ts deleted file mode 100644 index 459eb8e..0000000 --- a/src/hooks/useElement.ts +++ /dev/null @@ -1,100 +0,0 @@ -import type { IPersonConfig } from '@/types/storeType' -import { rgba } from '@/utils/color' - -export function useElementStyle(element: any, person: IPersonConfig, index: number, patternList: number[], patternColor: string, cardColor: string, cardSize: { width: number, height: number }, textSize: number, mod: 'default' | 'lucky' | 'sphere' = 'default', type: 'add' | 'change' = 'add') { - if (patternList.includes(index + 1) && mod === 'default') { - element.style.backgroundColor = rgba(patternColor, Math.random() * 0.2 + 0.8) - } - else if (mod === 'sphere' || mod === 'default') { - element.style.backgroundColor = rgba(cardColor, Math.random() * 0.5 + 0.25) - } - else if (mod === 'lucky') { - element.style.backgroundColor = rgba(cardColor, 0.8) - } - element.style.border = `1px solid ${rgba(cardColor, 0.25)}` - element.style.boxShadow = `0 0 12px ${rgba(cardColor, 0.5)}` - element.style.width = `${cardSize.width}px` - element.style.height = `${cardSize.height}px` - if (mod === 'lucky') { - element.className = 'lucky-element-card' - } - else { - element.className = 'element-card' - } - if (type === 'add') { - element.addEventListener('mouseenter', (ev: MouseEvent) => { - const target = ev.target as HTMLElement - target.style.border = `1px solid ${rgba(cardColor, 0.75)}` - target.style.boxShadow = `0 0 12px ${rgba(cardColor, 0.75)}` - }) - element.addEventListener('mouseleave', (ev: MouseEvent) => { - const target = ev.target as HTMLElement - target.style.border = `1px solid ${rgba(cardColor, 0.25)}` - target.style.boxShadow = `0 0 12px ${rgba(cardColor, 0.5)}` - }) - } - element.children[0].style.fontSize = `${textSize * 0.5}px` - if (person.uid) { - element.children[0].textContent = person.uid - } - - element.children[1].style.fontSize = `${textSize}px` - element.children[1].style.lineHeight = `${textSize * 3}px` - element.children[1].style.textShadow = `0 0 12px ${rgba(cardColor, 0.95)}` - if (person.name) { - element.children[1].textContent = person.name - } - // element.children[2].style.fontSize = `${textSize * 0.5}px` - // if (person.department || person.identity) { - // element.children[2].innerHTML = `${person.department ? person.department : ''}
${person.identity ? person.identity : ''}` - // } - - element.children[2].style.fontSize = `${textSize * 0.5}px` - // 设置部门和身份的默认值 - element.children[2].innerHTML = '' - if (person.department || person.identity) { - element.children[2].innerHTML = `${person.department ? person.department : ''}
${person.identity ? person.identity : ''}` - } - element.children[3].src = person.avatar - return element -} - -/** - * @description 设置抽中卡片的位置 - * 最少一个,最大十个 - */ -// TODO:不超过5个时:单行排列;超过5个时,6:上3下3;7:上3下4;8:上3下5;9:上4下5;10:上5下5 -export function useElementPosition(element: any, count: number, totalCount: number, cardSize: { width: number, height: number }, windowSize: { width: number, height: number }, cardIndex: number) { - let xTable = 0 - let yTable = 0 - const centerPosition = { - x: 0, - y: windowSize.height / 2 - cardSize.height * 0.9, - } - // 有一行为偶数的特殊数量 - const specialPosition = [2, 4, 7, 9] - // 不包含特殊值的 和 分两行中第一行为奇数值的 - if (!specialPosition.includes(totalCount) || (totalCount > 5 && cardIndex < 5)) { - const index = cardIndex % 5 - if (index === 0) { - xTable = centerPosition.x - yTable = centerPosition.y - Math.floor(cardIndex / 5) * (cardSize.height + 60) - } - else { - xTable = index % 2 === 0 ? Math.ceil(index / 2) * (cardSize.width + 100) : -Math.ceil(index / 2) * (cardSize.width + 100) - yTable = centerPosition.y - Math.floor(cardIndex / 5) * (cardSize.height + 60) - } - } - else { - const index = cardIndex % 5 - if (index === 0) { - xTable = centerPosition.x + (cardSize.width + 100) / 2 - yTable = centerPosition.y - Math.floor(cardIndex / 5) * (cardSize.height + 60) - } - else { - xTable = index % 2 === 0 ? Math.ceil(index / 2) * (cardSize.width + 100) + (cardSize.width + 100) / 2 : -(Math.ceil(index / 2) * (cardSize.width + 100)) + (cardSize.width + 100) / 2 - yTable = centerPosition.y - Math.floor(cardIndex / 5) * (cardSize.height + 60) - } - } - return { xTable, yTable } -} diff --git a/src/hooks/useElement/index.ts b/src/hooks/useElement/index.ts new file mode 100644 index 0000000..043a356 --- /dev/null +++ b/src/hooks/useElement/index.ts @@ -0,0 +1,323 @@ +import type { IPersonConfig } from '@/types/storeType' +import { rgba } from '@/utils/color' + +interface IUseElementStyle { + element: any + person: IPersonConfig + index: number + patternList: number[] + patternColor: string + cardColor: string + cardSize: { width: number, height: number } + scale: number + textSize: number + mod: 'default' | 'lucky' | 'sphere' + type?: 'add' | 'change' + +} +export function useElementStyle(props: IUseElementStyle) { + const { element, person, index, patternList, patternColor, cardColor, cardSize, scale, textSize, mod, type } = props + if (patternList.includes(index + 1) && mod === 'default') { + element.style.backgroundColor = rgba(patternColor, Math.random() * 0.2 + 0.8) + } + else if (mod === 'sphere' || mod === 'default') { + element.style.backgroundColor = rgba(cardColor, Math.random() * 0.5 + 0.25) + } + else if (mod === 'lucky') { + element.style.backgroundColor = rgba(cardColor, 0.8) + } + element.style.border = `1px solid ${rgba(cardColor, 0.25)}` + element.style.boxShadow = `0 0 12px ${rgba(cardColor, 0.5)}` + element.style.width = `${cardSize.width * scale}px` + element.style.height = `${cardSize.height * scale}px` + if (mod === 'lucky') { + element.className = 'lucky-element-card' + } + else { + element.className = 'element-card' + } + if (type === 'add') { + element.addEventListener('mouseenter', (ev: MouseEvent) => { + const target = ev.target as HTMLElement + target.style.border = `1px solid ${rgba(cardColor, 0.75)}` + target.style.boxShadow = `0 0 12px ${rgba(cardColor, 0.75)}` + }) + element.addEventListener('mouseleave', (ev: MouseEvent) => { + const target = ev.target as HTMLElement + target.style.border = `1px solid ${rgba(cardColor, 0.25)}` + target.style.boxShadow = `0 0 12px ${rgba(cardColor, 0.5)}` + }) + } + element.children[0].style.fontSize = `${textSize * scale * 0.5}px` + if (person.uid) { + element.children[0].textContent = person.uid + } + + element.children[1].style.fontSize = `${textSize * scale}px` + element.children[1].style.lineHeight = `${textSize * scale * 3}px` + element.children[1].style.textShadow = `0 0 12px ${rgba(cardColor, 0.95)}` + if (person.name) { + element.children[1].textContent = person.name + } + + element.children[2].style.fontSize = `${textSize * scale * 0.5}px` + // 设置部门和身份的默认值 + element.children[2].innerHTML = '' + if (person.department || person.identity) { + element.children[2].innerHTML = `${person.department ? person.department : ''}
${person.identity ? person.identity : ''}` + } + element.children[3].src = person.avatar + return element +} +interface CardRule { + [key: number]: { + maxLine: number + scale: number + rule: number[] + length: number + } +} +const cardRule: CardRule = { + 1: { + maxLine: 5, + scale: 2, + rule: [1], + length: 1, + }, + 2: { + maxLine: 5, + scale: 2, + rule: [2], + length: 1, + }, + 3: { + maxLine: 5, + scale: 2, + rule: [3], + length: 1, + }, + 4: { + maxLine: 5, + scale: 2, + rule: [4], + length: 1, + }, + 5: { + maxLine: 5, + scale: 2, + rule: [5], + length: 1, + }, + 6: { + maxLine: 3, + scale: 2, + rule: [3, 3], + length: 2, + }, + 7: { + maxLine: 4, + scale: 2, + rule: [3, 4], + length: 2, + }, + 8: { + maxLine: 5, + scale: 2, + rule: [3, 5], + length: 2, + }, + 9: { + maxLine: 5, + scale: 2, + rule: [4, 5], + length: 2, + }, + 10: { + maxLine: 5, + scale: 2, + rule: [5, 5], + length: 2, + }, + 11: { + maxLine: 6, + scale: 1.8, + rule: [5, 6], + length: 2, + }, + 12: { + maxLine: 6, + scale: 1.8, + rule: [6, 6], + length: 2, + }, + 13: { + maxLine: 7, + scale: 1.6, + rule: [6, 7], + length: 2, + }, + 14: { + maxLine: 7, + scale: 1.6, + rule: [7, 7], + length: 2, + }, + 15: { + maxLine: 8, + scale: 1.5, + rule: [7, 8], + length: 2, + }, + 16: { + maxLine: 8, + scale: 1.5, + rule: [8, 8], + length: 2, + }, + 17: { + maxLine: 6, + scale: 1.8, + rule: [5, 6, 6], + length: 3, + }, + 18: { + maxLine: 6, + scale: 1.8, + rule: [6, 6, 6], + length: 3, + }, + 19: { + maxLine: 7, + scale: 1.6, + rule: [6, 6, 7], + length: 3, + }, + 20: { + maxLine: 5, + scale: 1.6, + rule: [6, 7, 7], + length: 3, + }, + 21: { + maxLine: 7, + scale: 1.6, + rule: [7, 7, 7], + length: 3, + }, + 22: { + maxLine: 8, + scale: 1.5, + rule: [7, 7, 8], + length: 3, + }, + 23: { + maxLine: 8, + scale: 1.5, + rule: [7, 8, 8], + length: 3, + }, + 24: { + maxLine: 8, + scale: 1.5, + rule: [8, 8, 8], + length: 3, + }, + 25: { + maxLine: 9, + scale: 1.3, + rule: [8, 8, 9], + length: 3, + }, + 26: { + maxLine: 9, + scale: 1.3, + rule: [8, 9, 9], + length: 3, + }, + 27: { + maxLine: 9, + scale: 1.3, + rule: [9, 9, 9], + length: 3, + }, + 28: { + maxLine: 10, + scale: 1.2, + rule: [9, 9, 10], + length: 3, + }, + 29: { + maxLine: 10, + scale: 1.2, + rule: [9, 10, 10], + length: 3, + }, + 30: { + maxLine: 10, + scale: 1.2, + rule: [10, 10, 10], + length: 3, + }, +} +/** + * @description 设置抽中卡片的位置 + */ +export function useElementPosition( + element: any, + count: number, + totalCount: number, + cardSize: { width: number, height: number }, + windowSize: { width: number, height: number }, + cardIndex: number, +): { + xTable: number + yTable: number + scale: number +} { + let xTable = 0 + let yTable = 0 + const centerPosition = { + x: 0, + y: windowSize.height / 2, + } + const { scale, rule, length } = cardRule[totalCount] + // 计算缩放后的卡片尺寸 + const scaledCardWidth = cardSize.width * scale + const scaledCardHeight = cardSize.height * scale + // 计算当前卡片在第几行(从0开始) + let currentRow = 0 + let cardIndexInRow = cardIndex // 当前卡片在其所在行中的索引 + + // 根据规则确定卡片在哪一行及行内索引 + let cumulativeCount = 0 + for (let i = 0; i < rule.length; i++) { + if (cardIndex < cumulativeCount + rule[i]) { + currentRow = i + cardIndexInRow = cardIndex - cumulativeCount + break + } + cumulativeCount += rule[i] + } + + // 计算当前行的卡片数量 + const cardsInCurrentRow = rule[currentRow] + + // 计算每行的垂直中心位置 + const verticalSpacing = scaledCardHeight * 1.1 // 垂直间距基于缩放后的高度 + // 计算整体高度并调整居中 + const totalHeight = (length - 1) * verticalSpacing + scaledCardHeight // 包含卡片本身的高度 + const centerYOffset = -totalHeight / 2 + + // 修改此处逻辑,确保当length=2时,两行围绕中心点对称分布 + centerPosition.y = windowSize.height / 2 - totalHeight / 2 + + yTable = centerPosition.y + currentRow * verticalSpacing + centerYOffset + scaledCardHeight / 2 // 添加卡片高度的一半作为修正 + // 计算当前行的水平居中偏移 + const horizontalSpacing = scaledCardWidth * 1.2 // 水平间距基于缩放后的宽度 + const rowWidth = (cardsInCurrentRow - 1) * horizontalSpacing + const offsetX = -rowWidth / 2 // 行内水平居中 + + xTable = centerPosition.x + offsetX + cardIndexInRow * horizontalSpacing + + return { xTable, yTable, scale } +} diff --git a/src/views/Home/useViewModel.ts b/src/views/Home/useViewModel.ts index 8124f9b..12d511b 100644 --- a/src/views/Home/useViewModel.ts +++ b/src/views/Home/useViewModel.ts @@ -146,7 +146,19 @@ export function useViewModel() { element.appendChild(avatarEmpty) } - element = useElementStyle(element, tableData.value[i], i, patternList.value, patternColor.value, cardColor.value, cardSize.value, textSize.value) + element = useElementStyle({ + element, + person: tableData.value[i], + index: i, + patternList: patternList.value, + patternColor: patternColor.value, + cardColor: cardColor.value, + cardSize: cardSize.value, + scale: 1, + textSize: textSize.value, + mod: 'default', + }, + ) const object = new CSS3DObject(element) object.position.x = Math.random() * 4000 - 2000 object.position.y = Math.random() * 4000 - 2000 @@ -201,7 +213,18 @@ export function useViewModel() { if (luckyCardList.value.length) { luckyCardList.value.forEach((cardIndex: any) => { const item = objects.value[cardIndex] - useElementStyle(item.element, {} as any, i, patternList.value, patternColor.value, cardColor.value, cardSize.value, textSize.value, 'sphere') + useElementStyle({ + element: item.element, + person: {} as any, + index: i, + patternList: patternList.value, + patternColor: patternColor.value, + cardColor: cardColor.value, + cardSize: cardSize.value, + scale: 1, + textSize: textSize.value, + mod: 'sphere', + }) }) } luckyTargets.value = [] @@ -553,7 +576,14 @@ export function useViewModel() { luckyCardList.value.push(cardIndex) const totalLuckyCount = luckyTargets.value.length const item = objects.value[cardIndex] - const { xTable, yTable } = useElementPosition(item, rowCount.value, totalLuckyCount, { width: cardSize.value.width * 2, height: cardSize.value.height * 2 }, windowSize, index) + const { xTable, yTable, scale } = useElementPosition( + item, + rowCount.value, + totalLuckyCount, + { width: cardSize.value.width, height: cardSize.value.height }, + windowSize, + index, + ) new TWEEN.Tween(item.position) .to({ x: xTable, @@ -562,7 +592,18 @@ export function useViewModel() { }, 1200) .easing(TWEEN.Easing.Exponential.InOut) .onStart(() => { - item.element = useElementStyle(item.element, person, cardIndex, patternList.value, patternColor.value, luckyColor.value, { width: cardSize.value.width * 2, height: cardSize.value.height * 2 }, textSize.value * 2, 'lucky') + item.element = useElementStyle({ + element: item.element, + person, + index: cardIndex, + patternList: patternList.value, + patternColor: patternColor.value, + cardColor: luckyColor.value, + cardSize: { width: cardSize.value.width, height: cardSize.value.height }, + scale, + textSize: textSize.value, + mod: 'lucky', + }) }) .start() .onComplete(() => { @@ -691,7 +732,19 @@ export function useViewModel() { if (!objects.value[cardRandomIndexArr[i]]) { continue } - objects.value[cardRandomIndexArr[i]].element = useElementStyle(objects.value[cardRandomIndexArr[i]].element, allPersonList.value[personRandomIndexArr[i]], cardRandomIndexArr[i], patternList.value, patternColor.value, cardColor.value, { width: cardSize.value.width, height: cardSize.value.height }, textSize.value, mod, 'change') + objects.value[cardRandomIndexArr[i]].element = useElementStyle({ + element: objects.value[cardRandomIndexArr[i]].element, + person: allPersonList.value[personRandomIndexArr[i]], + index: cardRandomIndexArr[i], + patternList: patternList.value, + patternColor: patternColor.value, + cardColor: cardColor.value, + cardSize: { width: cardSize.value.width, height: cardSize.value.height }, + textSize: textSize.value, + scale: 1, + mod, + type: 'change', + }) } }, 200) }