diff --git a/README.md b/README.md index a5746ce..affec36 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ log-lottery是一个可配置可定制化的抽奖应用,炫酷3D球体,可 > 如果进入网站遇到图片无法显示或有报错的情况,请先到【全局配置】-【界面配置】菜单中点击【重置所有数据】按钮清除数据后进行更新。 +> 不支持内定功能 + ## 要求 使用PC端最新版Chrome或Edge浏览器。 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/style/style.scss b/src/style/style.scss index 38767df..342e40a 100644 --- a/src/style/style.scss +++ b/src/style/style.scss @@ -2,6 +2,7 @@ cursor: default; text-align: center; user-select: none; + .card-id { position: absolute; top: 10px; @@ -21,7 +22,7 @@ z-index: 2; } - .card-avatar-name{ + .card-avatar-name { top: auto; bottom: 0; left: 0px; @@ -37,7 +38,7 @@ z-index: 2; } - .card-avatar{ + .card-avatar { position: absolute; top: 0; left: 0; @@ -52,6 +53,8 @@ .lucky-element-card { cursor: default; + user-select: none; + &::before { background-color: linear-gradient(-45deg, #e81cff 0%, #40c9ff 100%); border: 1px solid linear-gradient(-45deg, #e81cff 0%, #40c9ff 100%); @@ -79,7 +82,8 @@ // animation: tracking-in-expand-fwd 0.8s cubic-bezier(0.215, 0.61, 0.355, 1) both; // animation-delay: 0.6s; } - .card-avatar-name{ + + .card-avatar-name { top: auto; bottom: 0; left: 0px; @@ -96,7 +100,7 @@ bottom: 15px; } - .card-avatar{ + .card-avatar { position: absolute; top: 0; left: 0; @@ -107,4 +111,4 @@ height: 240px !important; object-fit: cover; } -} +} \ No newline at end of file diff --git a/src/views/Config/Person/PersonAll/useViewModel.ts b/src/views/Config/Person/PersonAll/useViewModel.ts index fefaf77..d4280e0 100644 --- a/src/views/Config/Person/PersonAll/useViewModel.ts +++ b/src/views/Config/Person/PersonAll/useViewModel.ts @@ -103,7 +103,7 @@ export function useViewModel({ exportInputFileRef }: { exportInputFileRef: Ref { 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 = [] @@ -485,7 +508,7 @@ export function useViewModel() { return } - luckyCount.value = 10 + // luckyCount.value = 10 // 自定义抽奖个数 let leftover = currentPrize.value.count - currentPrize.value.isUsedCount @@ -498,7 +521,7 @@ export function useViewModel() { } } } - luckyCount.value = leftover < luckyCount.value ? leftover : luckyCount.value + luckyCount.value = leftover // 重构抽奖函数 luckyTargets.value = getRandomElements(personPool.value, luckyCount.value) luckyTargets.value.forEach((item) => { @@ -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) }