Files
JianSu-Naming/miniprogram/pages/index/index.js
2026-04-16 11:25:29 +08:00

243 lines
7.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const EXIT_THRESHOLD = 80;
const FAST_SWIPE_VELOCITY = 0.5;
Page({
isDragging: false,
isExiting: false,
startTime: 0,
startX: 0,
startY: 0,
lastX: 0,
data: {
nameList: [],
currentIndex: 0,
isLoading: true,
isFlipped: false,
keyword: '清冷',
// 动画控制
translateX: 0,
rotate: 0,
opacity: 1,
transition: 'none',
cardKey: 0,
collectedNames: [],
showCollection: false
},
onLoad(options) {
const keyword = options.keyword || this.data.keyword;
this.setData({ keyword });
this.fetchNames(keyword);
},
fetchNames(keyword) {
this.setData({ isLoading: true });
wx.showLoading({ title: '见素正在感悟...', mask: true });
wx.request({
url: 'http://localhost:8080/api/names/generate',
data: { keyword },
success: (res) => {
if (res.statusCode === 200 && res.data && res.data.length > 0) {
this.setData({ nameList: res.data, currentIndex: 0, isLoading: false, cardKey: this.data.cardKey + 1 });
} else {
wx.showToast({ title: '意境未达,请重试', icon: 'none' });
}
},
fail: () => wx.showToast({ title: '网络疏离,请检查后端', icon: 'none' }),
complete: () => { this.setData({ isLoading: false }); wx.hideLoading(); }
});
},
onFlip() {
// 守卫如果这是一次拖拽isDragging 会为 true则不执行翻转
if (this.isDragging || this.isExiting) return;
getApp().playAudio('flip');
this.setData({ isFlipped: !this.data.isFlipped });
},
onTouchStart(e) {
if (this.isExiting) return;
this.isDragging = false; // 每次开始触摸时,都假定为点击,而非拖拽
this.startX = e.touches[0].clientX;
this.lastX = this.startX;
this.startY = e.touches[0].clientY;
this.startTime = Date.now();
this.setData({ transition: 'none' });
},
onTouchMove(e) {
if (this.isExiting) return;
const deltaX = e.touches[0].clientX - this.startX;
// 只有移动超过5px才真正判定为“拖拽”
if (Math.abs(deltaX) > 5) {
this.isDragging = true;
}
// 如果不是拖拽,则不进行任何移动
if (!this.isDragging) return;
this.lastX = e.touches[0].clientX;
const rotate = deltaX * 0.05;
const opacity = 1 - Math.abs(deltaX) / 200;
this.setData({
translateX: deltaX,
rotate: rotate,
opacity: opacity
});
},
onTouchEnd() {
if (this.isExiting) return;
// 如果不是拖拽(即这是一次纯点击),则 onTouchEnd 不执行任何操作,交由 onFlip 处理
if (!this.isDragging) {
return;
}
// 以下是拖拽结束后的逻辑
const deltaX = this.lastX - this.startX;
const deltaTime = Date.now() - this.startTime;
const velocity = deltaX / deltaTime;
const shouldExit = Math.abs(deltaX) > EXIT_THRESHOLD || Math.abs(velocity) > FAST_SWIPE_VELOCITY;
if (shouldExit) {
this.isExiting = true;
const direction = deltaX > 0 ? 1 : -1;
const targetX = direction * 500;
this.setData({
translateX: targetX,
opacity: 0,
transition: 'all 0.3s cubic-bezier(0.6, -0.28, 0.735, 0.045)'
});
} else {
this.setData({
translateX: 0,
rotate: 0,
opacity: 1,
transition: 'all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275)'
});
}
// 延迟重置 isDragging 标志位。
// 这是为了确保在系统触发 tap 事件时isDragging 标志仍然为 true
// 从而让 onFlip 方法可以正确地忽略掉这次由拖拽产生的 tap。
setTimeout(() => {
this.isDragging = false;
}, 100);
},
onTransitionEnd() {
if (!this.isExiting) return;
const direction = this.data.translateX > 0 ? 'like' : 'dislike';
if (direction === 'like') {
this.handleLike();
} else {
this.handleCardExit(direction);
}
},
handleLike() {
wx.vibrateShort({ type: 'medium' });
getApp().playAudio('success');
const card = this.data.nameList[this.data.currentIndex];
if (!this.data.collectedNames.some(item => item.name === card.name)) {
this.setData({ collectedNames: [...this.data.collectedNames, card] });
}
this.handleCardExit('like');
},
handleCardExit() {
const nextIndex = this.data.currentIndex + 1;
wx.nextTick(() => {
this.setData({
transition: 'none',
translateX: 0,
rotate: 0,
opacity: 1,
isFlipped: false
}, () => {
if (nextIndex < this.data.nameList.length) {
this.setData({ currentIndex: nextIndex, cardKey: this.data.cardKey + 1 });
} else {
wx.showModal({
title: '见素时刻',
content: '这一波灵感已尽,是否再求几名?',
confirmText: '再求', cancelText: '返回',
success: (res) => { if (res.confirm) this.fetchNames(this.data.keyword); }
});
}
this.isExiting = false;
});
});
},
toggleCollectionView() {
this.setData({ showCollection: !this.data.showCollection });
},
onDeleteCollected(e) {
const nameToDelete = e.currentTarget.dataset.name;
this.setData({
collectedNames: this.data.collectedNames.filter(item => item.name !== nameToDelete)
});
wx.vibrateShort({ type: 'light' });
},
onSavePoster() {
const card = this.data.nameList[this.data.currentIndex];
wx.showLoading({ title: '绘笔收录中...', mask: true });
const query = wx.createSelectorQuery().in(this);
query.select('#posterCanvas')
.fields({ node: true, size: true })
.exec((res) => {
const canvas = res[0].node;
const ctx = canvas.getContext('2d');
const dpr = wx.getSystemInfoSync().pixelRatio;
canvas.width = 750 * dpr;
canvas.height = 1334 * dpr;
ctx.scale(dpr, dpr);
ctx.fillStyle = '#FFFFFF';
ctx.fillRect(0, 0, 750, 1334);
ctx.fillStyle = '#2D2D2D';
ctx.font = '300 120px serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const name = card.name;
const charArray = name.split('');
const charHeight = 140;
const startY = 667 - (charArray.length * charHeight) / 2 + charHeight / 2;
charArray.forEach((char, index) => ctx.fillText(char, 375, startY + index * charHeight));
const sealChar = this.data.keyword.substring(0, 1) || '素';
ctx.fillStyle = '#B22222';
ctx.fillRect(100, 100, 80, 80);
ctx.fillStyle = '#FFFFFF';
ctx.font = '40px serif';
ctx.fillText(sealChar, 140, 140);
const randomNum = Math.floor(Math.random() * 9000) + 1000;
ctx.fillStyle = '#D0D0D0';
ctx.font = '24px sans-serif';
ctx.fillText(`见素第 ${randomNum} 号灵感`, 375, 1200);
setTimeout(() => {
wx.canvasToTempFilePath({
canvas: canvas,
success: (res) => {
wx.hideLoading();
wx.showShareImageMenu({ path: res.tempFilePath });
},
fail: () => {
wx.hideLoading();
wx.showToast({ title: '绘笔受阻', icon: 'none' });
}
});
}, 300);
});
}
});