<template>
<view class="container">
<!-- 光晕效果:添加在qian1后面 -->
<view class="wave-container" :class="{'fade-out': waveFading}" v-show="showWave">
<view
v-for="i in waveCount"
:key="i"
class="wave"
:style="{
animationDelay: `${i * 0.5}s`
}"
></view>
</view>
<image
v-show="showQian1"
class="zhongQian zhongShake-animation"
@animationend="handleAnimationEnd"
src="https://shoujinshang.oss-cn-beijing.aliyuncs.com/static/luckyHomeImg/qian1.png"
mode="widthFix"></image>
<view class="qianXq" :class="{'scale-in': showQianXq}" v-show="showQianXq" id="save-container">
<!-- 原有内容保持不变 -->
<image class="bg" src="https://shoujinshang.oss-cn-beijing.aliyuncs.com/static/bestFortune/bg.png" mode="widthFix"></image>
<image class="hudie" src="https://shoujinshang.oss-cn-beijing.aliyuncs.com/static/bestFortune/hudie1.gif" mode="widthFix"></image>
<image class="hudian2" src="https://shoujinshang.oss-cn-beijing.aliyuncs.com/static/bestFortune/hudian2.webp" mode="widthFix"></image>
<image class="hudie1" src="https://shoujinshang.oss-cn-beijing.aliyuncs.com/static/bestFortune/hudie1.gif" mode="widthFix"></image>
<image class="titleBg" src="https://shoujinshang.oss-cn-beijing.aliyuncs.com/static/bestFortune/titleBg.png" mode="widthFix"></image>
<image class="qianXian" src="https://shoujinshang.oss-cn-beijing.aliyuncs.com/static/bestFortune/qianXian.png" mode="widthFix"></image>
<image class="bestQian" src="https://shoujinshang.oss-cn-beijing.aliyuncs.com/static/bestFortune/qian.png" mode="widthFix"></image>
<image class="shai" src="https://shoujinshang.oss-cn-beijing.aliyuncs.com/static/bestFortune/shai.png" mode="widthFix"></image>
<image class="fenxiangText" src="https://shoujinshang.oss-cn-beijing.aliyuncs.com/static/bestFortune/fenxiangText.png" mode="widthFix"></image>
<image class="xiangce" src="https://shoujinshang.oss-cn-beijing.aliyuncs.com/static/bestFortune/xiangce.png" @click="shareClick" mode="widthFix"></image>
<view class="title">
<view class="title1">{{ titleArray[0] }}</view>
<view class="title2">{{ titleArray[1] }}</view>
<view class="title3">{{ titleArray[2] }}</view>
</view>
<view class="time">{{currentDate }}</view>
<view
class="qianCon"
:class="{
'qianCon-centered': isQianConCentered,
'qianCon-original': !isQianConCentered,
[`qianCon-${signCharCount}`]: true
}"
>{{ signContent.sign }}</view>
<!-- 修改jiyu,添加动画类 -->
<view
class="jiyu"
:class="{'jiyu-slide-in': showJiyu}"
v-show="showJiyu"
>
<view class="jiyu1">{{ signDescPart1 }}</view>
<view class="jiyu2">{{ signDescPart2 }}</view>
</view>
<view class="qianText">属于你的·专属签</view>
<image class="close" src="https://shoujinshang.oss-cn-beijing.aliyuncs.com/static/bestFortune/close.png" mode="" @click="handleClose"></image>
</view>
<!-- 画布容器,用于生成图片 -->
<canvas
style="position: fixed; left: -10000rpx; top: -10000rpx;"
canvas-id="shareCanvas"
id="shareCanvas"
></canvas>
</view>
</template>
<script setup>
import { ref, onMounted, defineProps, defineEmits, computed, nextTick } from 'vue';
// 定义props,接收父组件传递的参数
const props = defineProps({
// 可以添加需要的props,比如签文内容
qianContent: {
type: String,
default: '暴 富'
}
});
// 定义emit事件
const emit = defineEmits(['close']);
// 控制显隐:初始显示qian1/光晕,隐藏qianXq
const showQian1 = ref(true);
const showWave = ref(true);
const showQianXq = ref(false);
const waveFading = ref(false); // 控制光晕淡出动画
const waveCount = 4;
// 新增状态变量
const isQianConCentered = ref(true); // 控制qianCon是否居中
const showJiyu = ref(false); // 控制jiyu是否显示
// 签文内容
const signContent = ref({
sign: '',
signDesc: '',
signType: null // 0:大吉签, 1:上上签
});
// 计算当前日期
const currentDate = computed(() => {
const now = new Date();
const year = now.getFullYear();
// 十天干
const heavenlyStems = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'];
// 十二地支
const earthlyBranches = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'];
// 计算干支纪年(以公元3年为基准点,3年为癸亥年,4年为甲子年)
const stemIndex = (year - 3) % 10;
const branchIndex = (year - 3) % 12;
// 处理负数情况(确保索引为非负)
const stem = heavenlyStems[(stemIndex + 10) % 10];
const branch = earthlyBranches[(branchIndex + 12) % 12];
const ganZhiYear = `${stem}${branch}年`;
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
return `${ganZhiYear} ${year}.${month}.${day}`;
});
// 计算标题数组
const titleArray = computed(() => {
if (signContent.value.signType === 0) {
return ['大', '吉', '签']; // 大吉签
} else {
return ['上', '上', '签']; // 上上签
}
});
// 计算签文描述的两个部分
const signDescPart1 = computed(() => {
const desc = signContent.value.signDesc || '';
// 去掉逗号、句号等标点符号
const cleanedDesc = desc.replace(/[,。、;,.;]/g, '');
const words = cleanedDesc.split('');
const midIndex = Math.ceil(words.length / 2);
return words.slice(0, midIndex).join('');
});
const signDescPart2 = computed(() => {
const desc = signContent.value.signDesc || '';
// 去掉逗号、句号等标点符号
const cleanedDesc = desc.replace(/[,。、;,.;]/g, '');
const words = cleanedDesc.split('');
const midIndex = Math.ceil(words.length / 2);
return words.slice(midIndex).join('');
});
// 设置签文内容的方法(供父组件调用)
const setSignContent = (content) => {
signContent.value = {
...signContent.value,
...content
};
};
const loadFontLocal = () => {
let fontSource = '';
try {
// 尝试获取plus对象
if (typeof plus !== 'undefined' && plus.io) {
fontSource = `url(${plus.io.convertLocalFileSystemURL('/static/luckyFonts/MaokenAssortedSans/MaoKenShiJinHei-2.ttf')})`;
} else {
fontSource = 'url(/static/luckyFonts/MaokenAssortedSans/MaoKenShiJinHei-2.ttf)';
}
} catch (error) {
// 出错时使用相对路径
fontSource = 'url(/static/luckyFonts/MaokenAssortedSans/MaoKenShiJinHei-2.ttf)';
}
uni.loadFontFace({
family: 'MaokenAssortedSans',
source: fontSource,
success() {
console.log('字体加载成功');
},
fail(e) {
console.log('字体加载失败', e);
}
});
};
const signCharCount = computed(() => {
const sign = signContent.value.sign || '';
return sign.replace(/\s/g, '').length;
});
// 动画结束:隐藏qian1/光晕,显示qianXq
const handleAnimationEnd = () => {
// 1. 先立即隐藏qian1
showQian1.value = false;
// 2. 等待0.5秒后,开始光晕淡出动画
setTimeout(() => {
waveFading.value = true; // 开始淡出动画
// 3. 在淡出动画结束后隐藏wave-container
setTimeout(() => {
showWave.value = false;
}, 500); // 淡出动画持续0.5秒
// 4. 在光晕淡出动画开始后,延迟0.2秒开始显示qianXq,让过渡更自然
setTimeout(() => {
showQianXq.value = true;
// 5. qianXq显示后,等待1秒开始后续动画
setTimeout(() => {
// 5.1 qianCon回到原来位置
isQianConCentered.value = false;
// 5.2 等待0.5秒后,jiyu从右侧滑入
setTimeout(() => {
showJiyu.value = true;
}, 500); // 等待qianCon回到原位后开始滑入
}, 1000); // 等待1秒
}, 200);
}, 500); // 等待0.5秒
};
// 关闭组件
const handleClose = () => {
emit('close');
};
// 重置组件状态(当重新显示时需要)
const resetComponent = () => {
showQian1.value = true;
showWave.value = true;
showQianXq.value = false;
waveFading.value = false;
isQianConCentered.value = true;
showJiyu.value = false;
// 重置签文内容为默认值
// signContent.value = {
// sign: '暴 富 福 福',
// signDesc: '所 念 皆 有 成 有',
// signType: 0
// };
};
onMounted(async () => {
loadFontLocal();
// 兜底:动画事件失效时,3秒后强制切换
setTimeout(() => {
if (showQian1.value) {
showQian1.value = false;
setTimeout(() => {
waveFading.value = true;
setTimeout(() => {
showWave.value = false;
}, 500);
setTimeout(() => {
showQianXq.value = true;
// 同样执行后续动画
setTimeout(() => {
isQianConCentered.value = false;
setTimeout(() => {
showJiyu.value = true;
}, 500);
}, 1000);
}, 200);
}, 500);
}
}, 3000);
});
// 暴露方法,用于直接显示详情(跳过动画)
const showDirectly = () => {
resetComponent(); // 先重置
// 手动设置状态,跳过动画
showQian1.value = false;
showWave.value = false;
showQianXq.value = true;
// 直接执行后续动画
setTimeout(() => {
isQianConCentered.value = false;
setTimeout(() => {
showJiyu.value = true;
}, 500);
}, 100);
};
// 暴露方法给父组件
defineExpose({
resetComponent,
setSignContent,
showDirectly // 新增方法
});
</script>
<style scoped>
.container {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
background: #000000;
/* opacity: 0.8; */
background: rgba(0, 0, 0, 0.8);
z-index: 1000;
}
.bg {
position: absolute;
width: 618rpx;
height: 990rpx;
left: 72rpx;
top: 174rpx;
opacity: 1;
}
/*调整wave-container的位置和大小*/
.wave-container {
position: absolute;
width: 500rpx; /* 扩大宽度 */
height: 800rpx; /* 扩大高度 */
display: flex;
align-items: center;
justify-content: center;
/* 将光晕放在qian1的背后中间位置 */
left: 390rpx; /* qian1的left */
top: 17%; /* qian1的top */
/* 使用transform让光晕容器居中在qian1位置 */
transform: translateX(-50%);
z-index: 1;
opacity: 1;
}
/* 修改2:调整wave的样式,使用淡金色 */
.wave {
position: absolute;
border-radius: 50%;
border: 3rpx solid; /* 加粗边框 */
/* 使用淡金色:#FFD700是金色,rgba(255, 215, 0, 0.8)是带透明度的金色 */
border-color: rgba(255, 215, 0, 0.6); /* 淡金色,透明度0.6 */
/* 添加内部阴影增强光晕效果 */
box-shadow: 0 0 20rpx rgba(255, 215, 0, 0.4) inset;
animation: waveSpread 3s infinite ease-in-out;
}
/* 光晕淡出动画 */
@keyframes fadeOut {
0% { opacity: 1; }
100% { opacity: 0; }
}
.wave-container.fade-out {
animation: fadeOut 0.5s ease-out forwards;
}
.wave-container.fade-out .wave {
animation-play-state: paused;
}
/*扩大waveSpread动画的范围 */
@keyframes waveSpread {
0%, 100% {
width: 150rpx; /* 扩大初始大小 */
height: 150rpx;
opacity: 0;
border-width: 4rpx; /* 开始时边框较粗 */
}
50% {
width: 700rpx; /* 大大扩大最大尺寸 */
height: 700rpx;
opacity: 0.8; /* 提高最大透明度 */
border-width: 1rpx; /* 扩散时边框变细 */
border-color: rgba(255, 215, 0, 0.3); /* 扩散时颜色变淡 */
box-shadow: 0 0 30rpx rgba(255, 215, 0, 0.2) inset; /* 扩散时阴影变淡 */
}
}
/* 签1样式 */
.zhongQian {
position: absolute;
width: 120rpx;
height: 520rpx;
left: 325rpx;
top: 30%;
z-index: 2; /* 确保在光晕前面 */
transform-origin: 50% 50%;
}
/* 晃动动画 */
@keyframes shake {
0% { transform: rotate(0deg); }
20% { transform: rotate(5deg); }
40% { transform: rotate(-5deg); }
60% { transform: rotate(3deg); }
80% { transform: rotate(-3deg); }
100% { transform: rotate(0deg); }
}
.zhongShake-animation {
animation: shake 0.5s ease-in-out 5;
animation-fill-mode: forwards;
}
/* 签详情样式 */
.qianXq {
position: absolute;
width: 100%;
height: 100%;
/* 初始状态:缩小到很小,完全透明 */
transform: scale(0.3);
opacity: 0;
/* 设置变换原点为中心 */
transform-origin: center center;
}
/* qianXq缩放显示动画 - 使用关键帧动画 */
@keyframes scaleIn {
0% {
transform: scale(0.3);
opacity: 0;
}
60% {
transform: scale(1.05);
opacity: 1;
}
80% {
transform: scale(0.95);
}
100% {
transform: scale(1);
opacity: 1;
}
}
/* 应用缩放动画 */
.qianXq.scale-in {
animation: scaleIn 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
}
.qianXq .titleBg {
position: absolute;
width: 200rpx;
height: 74rpx;
left: 282rpx;
top: 406rpx;
}
.qianXian {
position: absolute;
width: 228rpx;
height: 12rpx;
left: 264rpx;
top: 490rpx;
}
.bestQian {
position: absolute;
width: 258rpx;
height: 30rpx;
left: 252rpx;
top: 898rpx;
}
.shai {
position: absolute;
width: 324rpx;
height: 110.3rpx;
left: 217.04rpx;
top: 1128rpx;
}
.fenxiangText {
position: absolute;
width: 182rpx;
height: 20rpx;
left: 288rpx;
top: 1196rpx;
}
.xiangce {
position: absolute;
width: 324rpx;
height: 106rpx;
left: 218rpx;
top: 1250rpx;
}
.title {
font-family: MaokenAssortedSans;
font-size: 44rpx;
color: #FDF8D1;
line-height: 52rpx;
text-align: left;
font-style: normal;
}
.title .title1 {
position: absolute;
left: 306rpx;
top: 410rpx;
}
.title .title2 {
position: absolute;
left: 356rpx;
top: 410rpx;
}
.title .title3 {
position: absolute;
left: 412rpx;
top: 410rpx;
}
.qianXq .hudie {
position: absolute;
transform: scaleX(-1);
width: 100.38rpx;
height: 78rpx;
left: 530rpx;
top: 288rpx;
}
.hudie1 {
position: absolute;
width: 110rpx;
height: 82rpx;
left: 82rpx;
top: 900rpx;
}
.qianXq .hudian2 {
position: absolute;
width: 100.38rpx;
height: 78rpx;
left: 150rpx;
top: 980rpx;
}
.time {
position: absolute;
width: 202rpx;
height: 34rpx;
left: 274rpx;
top: 498rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 24rpx;
color: #C69D5E;
line-height: 34rpx;
text-align: left;
font-style: normal;
}
/* qianCon基础样式 */
.qianCon {
position: absolute;
top: 630rpx;
height: 168rpx;
font-family: MaokenAssortedSans;
color: #B78543;
line-height: 84rpx;
text-align: left;
font-style: normal;
transition: all 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
display: flex;
align-items: center;
/* writing-mode: vertical-rl;
text-orientation: upright;
white-space: nowrap; */
}
/* 2个字:默认样式 */
.qianCon.qianCon-2 {
left: 290rpx;
width: 72rpx;
font-size: 70rpx;
}
/* 3个字:调整位置和大小 */
.qianCon.qianCon-3 {
left: 280rpx; /* 左移10rpx */
width: 72rpx;
font-size: 65rpx; /* 稍小一点 */
}
/* 4个字:进一步调整 */
.qianCon.qianCon-4 {
left: 270rpx; /* 再左移10rpx */
width: 72rpx;
font-size: 60rpx; /* 再小一点 */
}
/* 5个字及以上:最小字体,居中显示 */
.qianCon.qianCon-5,
.qianCon.qianCon-6,
.qianCon.qianCon-7,
.qianCon.qianCon-8 {
left: 50%;
transform: translateX(-50%);
width: 72rpx;
font-size: 55rpx;
text-align: center;
}
/* qianCon居中状态 */
.qianCon.qianCon-centered {
left: 50% !important;
top: 45%;
width: 72rpx;
transform: translate(-50%, -50%) !important;
font-size: 80rpx !important;
text-align: center !important;
}
/* qianCon居中状态 */
/* .qianCon.qianCon-centered {
left: 50%;
top: 45%;
transform: translate(-50%, -50%);
font-size: 80rpx;
text-align: center;
} */
/* qianCon回到原位的动画 */
.qianCon.qianCon-original {
left: 290rpx;
top: 630rpx;
width: 72rpx;
transform: translate(0, 0);
font-size: 70rpx;
text-align: left;
}
/* 修改jiyu容器样式 */
.jiyu {
position: absolute;
/* 初始位置在屏幕右侧外 */
left: 750rpx;
top: 596rpx;
/* 隐藏jiyu内容,等滑入后再显示 */
opacity: 0;
/* 滑入动画 */
transition: all 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
/* jiyu滑入动画 */
.jiyu.jiyu-slide-in {
left: 390rpx;
opacity: 1;
}
.jiyu1 {
position: absolute;
left: 0rpx;
top: 0rpx;
width: 30rpx;
height: 240rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 28rpx;
color: #B78543;
line-height: 40rpx;
text-align: left;
font-style: normal;
}
.jiyu2 {
position: absolute;
left: 44rpx;
top: 2rpx;
width: 30rpx;
height: 240rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 28rpx;
color: #B78543;
line-height: 40rpx;
text-align: left;
font-style: normal;
}
.qianText {
position: absolute;
left: 296rpx;
top: 944rpx;
width: 168rpx;
height: 32rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 500;
font-size: 22rpx;
color: #C69D5E;
line-height: 32rpx;
text-align: left;
font-style: normal;
}
.close {
position: absolute;
width: 60rpx;
height: 60rpx;
left: 346rpx;
top: 1390rpx;
}
/* 添加样式确保生成图片时布局正常 */
#save-container {
position: relative;
width: 750rpx;
height: 1334rpx;
background: transparent;
}
</style>
实现点击shareClick,将页面截图为图片,保存到手机相册中,使用的技术是u你app、
最新发布