feat: 修改自动领取收益关闭,修复下架时间的bug,修改的光栅卡陀螺仪

This commit is contained in:
zerosaturation 2026-05-25 11:08:35 +08:00
parent ce4fd85926
commit 2855cd512d
12 changed files with 216 additions and 55 deletions

View File

@ -491,10 +491,9 @@ func (s *assetService) GetAsset(req *pb.GetAssetRequest, userID, starID int64) (
if exhibitionStartTime == 0 {
exhibitionStartTime = asset.CreatedAt // 兜底
}
earnings = calculateRealtimeEarnings(asset.LikeCount, exhibitionStartTime, time.Now().UnixMilli())
// 获取展出过期时间
// 获取展出过期时间(先获取,用于计算收益)
exhibitionExpireAt, _ = s.assetRepo.GetExhibitionExpireTime(asset.ID)
earnings = calculateRealtimeEarnings(asset.LikeCount, exhibitionStartTime, time.Now().UnixMilli(), exhibitionExpireAt)
}
// 6.5 从 asset_registry 表获取 grade
@ -759,9 +758,16 @@ func calculateHourlyEarnings(likeCount int32) float64 {
// calculateRealtimeEarnings 实时计算展示收益
// 公式R1 = R0 × T × [100% + Buff(n)]
// R0 = 5 水晶/小时T = 上架时长小时Buff(n) 根据点赞数计算
func calculateRealtimeEarnings(likeCount int32, startTime, now int64) int64 {
// 注意:使用 min(now, expireAt) 确保过期后收益不再增长
func calculateRealtimeEarnings(likeCount int32, startTime, now, expireAt int64) int64 {
// 计算有效截止时间(展览结束时间 vs 当前时间,取较小值)
endTime := now
if expireAt > 0 && expireAt < now {
endTime = expireAt
}
// 计算上架时长(毫秒转小时)
T := (now - startTime) / 3600000
T := (endTime - startTime) / 3600000
if T <= 0 {
T = 1 // 最少1小时
}

View File

@ -452,7 +452,7 @@ func (r *galleryRepository) GetMyExhibitedAssets(userID, starID int64, page, pag
now := time.Now().UnixMilli()
for _, item := range items {
item.HourlyEarnings = calculateHourlyEarnings(item.LikeCount)
item.Earnings = calculateRealtimeEarnings(item.LikeCount, item.ExhibitedAt, now)
item.Earnings = calculateRealtimeEarnings(item.LikeCount, item.ExhibitedAt, now, item.ExpireAt)
}
return items, total, nil
@ -496,7 +496,7 @@ func (r *galleryRepository) GetUserExhibitedAssets(userID, starID int64, page, p
// 实时计算每个资产的收益
for _, item := range items {
item.HourlyEarnings = calculateHourlyEarnings(item.LikeCount)
item.Earnings = calculateRealtimeEarnings(item.LikeCount, item.ExhibitedAt, now)
item.Earnings = calculateRealtimeEarnings(item.LikeCount, item.ExhibitedAt, now, item.ExpireAt)
}
return items, total, nil
@ -660,9 +660,16 @@ func calculateHourlyEarnings(likeCount int32) float64 {
// calculateRealtimeEarnings 实时计算展示收益
// 公式R1 = R0 × T × [100% + Buff(n)]
// R0 = 5 水晶/小时T = 上架时长小时Buff(n) 根据点赞数计算
func calculateRealtimeEarnings(likeCount int32, startTime, now int64) int64 {
// 注意:使用 min(now, expireAt) 确保过期后收益不再增长
func calculateRealtimeEarnings(likeCount int32, startTime, now, expireAt int64) int64 {
// 计算有效截止时间(展览结束时间 vs 当前时间,取较小值)
endTime := now
if expireAt > 0 && expireAt < now {
endTime = expireAt
}
// 计算上架时长(毫秒转小时)
T := (now - startTime) / 3600000
T := (endTime - startTime) / 3600000
if T <= 0 {
T = 1 // 最少1小时
}

View File

@ -111,7 +111,7 @@ func main() {
logger.Logger.Info("Services initialized")
// 7. Init workergoroutine 中启动)
resetWorker := worker.NewDailyResetWorker(dailyRepo, revenueRepo, userRPCClient)
resetWorker := worker.NewDailyResetWorker(dailyRepo, revenueRepo, userRPCClient, galleryRPCClient)
go resetWorker.Start()
logger.Logger.Info("Reset worker started")

View File

@ -80,6 +80,7 @@ func (s *revenueService) GetExhibitionRevenue(ctx context.Context, userID, starI
CycleStartTime: r.CycleStartTime,
CycleEndTime: r.CycleEndTime,
Status: r.Status,
CanClaim: r.Status == "claimable",
}
items = append(items, item)
}

View File

@ -18,6 +18,7 @@ type DailyResetWorker struct {
dailyRepo repository.DailyTaskRepository
revenueRepo repository.RevenueRepository
userClient client.UserServiceClient
galleryClient client.GalleryServiceClient
stopCh chan struct{}
wg sync.WaitGroup
}
@ -26,11 +27,13 @@ func NewDailyResetWorker(
dailyRepo repository.DailyTaskRepository,
revenueRepo repository.RevenueRepository,
userClient client.UserServiceClient,
galleryClient client.GalleryServiceClient,
) *DailyResetWorker {
return &DailyResetWorker{
dailyRepo: dailyRepo,
revenueRepo: revenueRepo,
userClient: userClient,
galleryClient: galleryClient,
stopCh: make(chan struct{}),
}
}
@ -98,8 +101,8 @@ func (w *DailyResetWorker) doResetAndAutoClaim() {
logger.Logger.Info(fmt.Sprintf("DailyResetWorker: daily tasks reset: %d records updated", resetCount))
}
// 2. 自动发放展示收益
w.autoClaimExhibitionRevenue()
// 2. 自动发放展示收益(暂时关闭)
// w.autoClaimExhibitionRevenue()
}
func (w *DailyResetWorker) autoClaimExhibitionRevenue() {
@ -128,6 +131,16 @@ func (w *DailyResetWorker) autoClaimExhibitionRevenue() {
zap.Int64("record_id", record.ID), zap.Error(err))
}
totalClaimed++
// 自动下架:调用 GalleryService 下架展览
if w.galleryClient != nil && record.AssetID > 0 {
if err := w.galleryClient.RemoveExhibitionByAsset(context.Background(), record.AssetID); err != nil {
logger.Logger.Warn("DailyResetWorker: failed to remove exhibition",
zap.Int64("asset_id", record.AssetID),
zap.Int64("revenue_record_id", record.ID),
zap.Error(err))
}
}
break
}
lastErr = err

View File

@ -0,0 +1,83 @@
# 光栅卡陀螺仪交互优化设计方案
**日期:** 2026-05-22
**文件:** `docs/superpowers/specs/2026-05-22-lenticular-gyro-optimization-design.md`
---
## 1. 问题描述
设备使用陀螺仪倾斜查看光栅卡时,图片在左右视图之间快速来回切换,无法稳定在某一侧。
**期望行为:**
- 设备居中(不倾斜)→ 显示中性图
- 往左倾斜 → 稳定显示左视图
- 往右倾斜 → 稳定显示右视图
**当前行为:**
- 轻微倾斜就触发切换,且图片在左右之间来回振荡
- 没有平衡点让图片稳定
---
## 2. 根本原因
**原因缺少中性阈值区Dead Zone**
陀螺仪数据直接映射到图片切换阈值,没有任何"容忍区"。
当设备略微偏离中心时:
- `gamma` 值微小的正负变化
- 直接导致 `x` 方向变化
- 触发 opacity 切换
加上陀螺仪本身的噪声和设备轻微晃动,导致图片不断在左右之间来回切换。
---
## 3. 修复方案
**文件:** `frontend/composables/useHolographicPreview.js`
### 修改:增大中性阈值区
当前代码已有 dead zone 判断逻辑,只需增大 `db` 系数即可:
```javascript
// 第79行
const db = 0.08 * dead // 从 0.016 改为 0.08
```
**说明:** 增大 db 系数 = 中性区变宽 = 更难触发切换 = 图片更稳定
---
## 4. 参数调整
| 参数 | 当前值 | 修改后 | 说明 |
|------|--------|--------|------|
| db (dead zone) | 0.016 | 0.08 | 中性区阈值,越大越难触发切换 |
**建议值范围:** 0.05 ~ 0.12
- 值越大:中性区越宽,越稳定但响应越慢
- 值越小:越敏感,但容易误触发
---
## 5. 验收标准
| 测试场景 | 期望结果 |
|---------|---------|
| 设备静止居中 | 显示中性图,稳定不动 |
| 轻微倾斜 | 保持中性,不触发切换 |
| 明显往左倾斜 | 显示左视图,稳定 |
| 明显往右倾斜 | 显示右视图,稳定 |
| 从倾斜缓慢回到居中 | 平滑过渡,无振荡 |
---
## 6. 影响范围
- 修改文件:`frontend/composables/useHolographicPreview.js`
- 不影响触摸滑动交互
- 不涉及后端或数据库

View File

@ -76,7 +76,7 @@ export function useHolographicPreview() {
const dy = (beta - accelBaseY) / 45 * sens
accelSmoothed = lerp(accelSmoothed, dx, k)
const dead = physics.sensorDeadzoneStrength || 1
const db = 0.016 * dead
const db = 0.08 * dead
simulate(
Math.abs(accelSmoothed) < db ? 0 : clamp(accelSmoothed, -1, 1),
clamp(dy, -1, 1)

View File

@ -9,7 +9,7 @@ const FLOW_PHYSICS = {
angleStability: 52,
transitionSmoothness: 40,
tiltSensitivity: 96,
sensorDeadzoneStrength: 0,
sensorDeadzoneStrength: 1,
parallaxDepth: 18,
lenticularAnchorFloor: 0.1,
lenticularNonDominantResidualMin: 0.092,
@ -18,7 +18,7 @@ const FLOW_PHYSICS = {
}
const DISCRETE_STEP_DEG = 13
const DISCRETE_STEP_HYST_DEG = 5
const DISCRETE_STEP_HYST_DEG = 9
const TILT_MAG_TAU_MS = 150
const TILT_MAG_MAX_RISE_DPS = 82
const TILT_MAG_MAX_FALL_DPS = 168
@ -51,6 +51,7 @@ export function useLenticularCraftTiltPreview(layersRef) {
}
function simulateFromSignedDegrees(tiltMagDeg, signedDegHint) {
console.log('[DEBUG simulateFromSignedDegrees] called, tiltMagDeg:', tiltMagDeg, 'signedDegHint:', signedDegHint)
const ls = getLayersArray()
const n = Math.max(1, ls.length)
const raw = Math.abs(Number(tiltMagDeg) || 0)
@ -69,6 +70,7 @@ export function useLenticularCraftTiltPreview(layersRef) {
const maxFall = TILT_MAG_MAX_FALL_DPS * sec
if (Number.isFinite(signedDegHint)) {
console.log('[DEBUG simulateFromSignedDegrees] signedDegHint is finite:', signedDegHint)
if (lastSignedDegHint != null && sec > 1e-6) {
const dAbsSignedDt =
(Math.abs(signedDegHint) - Math.abs(lastSignedDegHint)) / sec
@ -94,8 +96,34 @@ export function useLenticularCraftTiltPreview(layersRef) {
const MARGIN = DISCRETE_STEP_HYST_DEG
const stepBefore = discreteStableStep.value
let s = stepBefore
while (absDeg >= (s + 1) * STEP + MARGIN) s++
while (s > 0 && absDeg <= s * STEP - MARGIN) s--
// 二值模式:仅允许 0(中性) 或 1(倾斜),防止同方向重复递增档位
const ENTER_THRESHOLD = STEP + MARGIN
const EXIT_THRESHOLD = MARGIN
if (s === 0) {
// 中性状态仅当倾斜超过阈值才进入档位1
if (absDeg >= ENTER_THRESHOLD) {
s = 1
console.log('[DEBUG] 进入档位1, absDeg:', absDeg.toFixed(2), 'signedDegHint:', signedDegHint)
}
} else if (s === 1) {
// 档位1状态检测方向变化才允许退回
// 如果 signedDegHint 符号改变(反方向倾斜),立即回到中性
if (lastSignedDegHint !== null && Number.isFinite(signedDegHint)) {
if (signedDegHint * lastSignedDegHint < 0) {
// 方向改变,回到中性
s = 0
lastSignedDegHint = signedDegHint
console.log('[DEBUG] 方向改变,回到中性, signedDegHint:', signedDegHint.toFixed(2))
}
} else if (absDeg <= EXIT_THRESHOLD) {
// 没有方向信息时,使用角度阈值
s = 0
console.log('[DEBUG] 角度不足,回到中性, absDeg:', absDeg.toFixed(2))
}
}
discreteStableStep.value = s
const idx = s % n
const sens = physics.tiltSensitivity / 100
@ -134,13 +162,17 @@ export function useLenticularCraftTiltPreview(layersRef) {
}
function scheduleTiltStart() {
console.log('[DEBUG useLenticularCraftTiltPreview] scheduleTiltStart called')
cancelScheduledTiltStart()
nextTick(() => {
relax(0.86)
lockPreviewStill()
console.log('[DEBUG useLenticularCraftTiltPreview] entryTiltTimer set, delay:', LENTICULAR_TILT_START_DELAY_MS)
entryTiltTimer = setTimeout(() => {
entryTiltTimer = null
console.log('[DEBUG useLenticularCraftTiltPreview] startTilt about to be called')
startTilt()
console.log('[DEBUG useLenticularCraftTiltPreview] startTilt called')
}, LENTICULAR_TILT_START_DELAY_MS)
})
}

View File

@ -361,6 +361,7 @@ export function useLenticularStudioTilt(opts) {
}
function startAccelInternal(options = {}) {
console.log('[DEBUG useLenticularStudioTilt] startAccelInternal called, options:', options)
const fromNativeFallback = options.fromNativeFallback === true
mode = 'accel'
stopAccel()
@ -379,34 +380,45 @@ export function useLenticularStudioTilt(opts) {
if (useStudioAccelDirect) {
if (typeof simulateFromSignedDegrees === 'function') {
console.log('[DEBUG useLenticularStudioTilt] 使用 simulateFromSignedDegrees')
resetStudioAccelBaseline()
}
accelHandler = (res) => {
console.log('[DEBUG useLenticularStudioTilt] accelHandler called, x:', res.x, 'y:', res.y, 'z:', res.z)
const ax = Number(res.x) || 0
const ay = Number(res.y) || 0
const az = Number(res.z) || 0
const gMag = Math.hypot(ax, ay, az)
if (gMag < 0.12) return
console.log('[DEBUG useLenticularStudioTilt] gMag:', gMag.toFixed(3))
if (gMag < 0.12) {
console.log('[DEBUG useLenticularStudioTilt] gMag too small, returning')
return
}
/* 竖屏常见握持:左右倾斜主要反映为 ax/az 与重力的关系(免 warmup避免长期无输出 */
if (typeof simulateFromSignedDegrees === 'function') {
console.log('[DEBUG useLenticularStudioTilt] 调用 simulateFromSignedDegrees')
const tiltRad = Math.atan2(-ax, az)
if (studioAccelBaseRad == null) {
console.log('[DEBUG useLenticularStudioTilt] 正在采集 baseline')
studioAccelBaselineRads.push(tiltRad)
if (studioAccelBaselineRads.length < STUDIO_ACCEL_BASELINE_FRAMES) {
simulate(0, 0)
return
}
studioAccelBaseRad = circularMeanRad(studioAccelBaselineRads)
console.log('[DEBUG useLenticularStudioTilt] baseline 采集完成:', studioAccelBaseRad)
studioAccelBaselineRads = []
}
let delta = tiltRad - studioAccelBaseRad
while (delta > Math.PI) delta -= 2 * Math.PI
while (delta < -Math.PI) delta += 2 * Math.PI
const signedDeg = delta * (180 / Math.PI)
console.log('[DEBUG useLenticularStudioTilt] delta:', delta.toFixed(4), 'signedDeg:', signedDeg.toFixed(2))
const rawAbs = Math.abs(signedDeg)
const prevLp = studioAccelDiscreteDegLp
const kLp = rawAbs >= prevLp ? 0.27 : 0.4
studioAccelDiscreteDegLp += (rawAbs - studioAccelDiscreteDegLp) * kLp
console.log('[DEBUG useLenticularStudioTilt] calling simulateFromSignedDegrees, mag:', studioAccelDiscreteDegLp.toFixed(2), 'signedDeg:', signedDeg.toFixed(2))
simulateFromSignedDegrees(studioAccelDiscreteDegLp, signedDeg)
} else {
const tiltRaw = Math.atan2(-ax, az) / (Math.PI / 5)
@ -446,13 +458,17 @@ export function useLenticularStudioTilt(opts) {
}
try {
uni.onAccelerometerChange(accelHandler)
console.log('[DEBUG useLenticularStudioTilt] onAccelerometerChange 注册成功')
const applyStart = (interval) => {
console.log('[DEBUG useLenticularStudioTilt] 调用 startAccelerometer, interval:', interval)
uni.startAccelerometer({
interval,
success: () => {
console.log('[DEBUG useLenticularStudioTilt] startAccelerometer 成功')
gyroSourceLabel.value = 'accelerometer'
},
fail: () => {
fail: (err) => {
console.log('[DEBUG useLenticularStudioTilt] startAccelerometer 失败:', JSON.stringify(err))
if (interval === 'game') {
applyStart('normal')
return
@ -463,11 +479,13 @@ export function useLenticularStudioTilt(opts) {
}
applyStart('game')
} catch (e) {
console.log('[DEBUG useLenticularStudioTilt] try onAccelerometerChange failed:', e)
gyroSourceLabel.value = 'simulation'
}
}
function onNativeAngleFrame(x, y, z) {
console.log('[DEBUG useLenticularStudioTilt] onNativeAngleFrame:', x, y, z)
const vx = Number(x)
const vy = Number(y)
const vz = Number(z)
@ -517,22 +535,27 @@ export function useLenticularStudioTilt(opts) {
}
function startNativeInternal() {
console.log('[DEBUG useLenticularStudioTilt] startNativeInternal called')
stopNative()
stopAccel()
resetNativeBaseline()
// #ifdef APP-PLUS
gyroModule = tryRequireImengyuGyro()
console.log('[DEBUG useLenticularStudioTilt] gyroModule:', gyroModule ? 'found' : 'not found')
if (!gyroModule) {
console.log('[DEBUG useLenticularStudioTilt] gyroModule not found, falling back to accel')
mode = 'accel'
startAccelInternal()
return
}
mode = 'native'
const myGen = tiltGen
console.log('[DEBUG useLenticularStudioTilt] native gyro mode set, calling startNativeGyro')
/** 与官方示例一致normal / ui / game / fastestgame≈50Hz */
const startOpts = { interval: 'game' }
scheduleNativeGyroStallFallback = () => {
console.log('[DEBUG useLenticularStudioTilt] scheduleNativeGyroStallFallback called')
if (nativeWatchdogTimer != null) {
try {
clearTimeout(nativeWatchdogTimer)
@ -541,18 +564,9 @@ export function useLenticularStudioTilt(opts) {
}
nativeWatchdogTimer = null
}
nativeWatchdogTimer = setTimeout(() => {
nativeWatchdogTimer = null
if (myGen !== tiltGen) return
if (mode !== 'native') return
console.warn(
'[useLenticularStudioTilt] native gyro stalled (no angle frames for ' +
NATIVE_GYRO_STALL_FALLBACK_MS +
'ms), falling back to accelerometer'
)
console.log('[DEBUG useLenticularStudioTilt] native gyro stalled, fallback to accelerometer')
stopNative()
startAccelInternal({ fromNativeFallback: true })
}, NATIVE_GYRO_STALL_FALLBACK_MS)
}
function invokeStartNativeGyro() {
@ -582,6 +596,7 @@ export function useLenticularStudioTilt(opts) {
if (useSync) {
const v = mod.getGyroValueSync()
if (v && hasAnglePayload(v)) {
console.log('[DEBUG useLenticularStudioTilt] getGyroValueSync:', JSON.stringify(v))
onNativeAngleFrame(v.x, v.y, v.z)
}
return
@ -589,6 +604,7 @@ export function useLenticularStudioTilt(opts) {
mod.getGyroValue((v) => {
if (myGen !== tiltGen || !gyroModule || !v) return
if (hasAnglePayload(v)) {
console.log('[DEBUG useLenticularStudioTilt] getGyroValue:', JSON.stringify(v))
onNativeAngleFrame(v.x, v.y, v.z)
}
})
@ -602,6 +618,7 @@ export function useLenticularStudioTilt(opts) {
let kickOnce = false
const kick = () => {
console.log('[DEBUG useLenticularStudioTilt] kick called, kickOnce:', kickOnce)
if (kickOnce) return
if (myGen !== tiltGen || !gyroModule) return
kickOnce = true
@ -617,10 +634,13 @@ export function useLenticularStudioTilt(opts) {
const usePoll =
typeof mod.startGyro === 'function' &&
(typeof mod.getGyroValue === 'function' || typeof mod.getGyroValueSync === 'function')
console.log('[DEBUG useLenticularStudioTilt] usePoll:', usePoll)
if (usePoll) {
console.log('[DEBUG useLenticularStudioTilt] calling mod.startGyro')
try {
mod.startGyro(startOpts, (res) => {
console.log('[DEBUG useLenticularStudioTilt] startGyro callback, res:', JSON.stringify(res))
if (myGen !== tiltGen || !gyroModule) return
/* 插件约定:首包只表示是否开启成功,不含持续角度;若回调无对象则无法进入官方要求的 getGyroValue 轮询 */
if (!res) {
@ -751,6 +771,7 @@ export function useLenticularStudioTilt(opts) {
}
function start() {
console.log('[DEBUG useLenticularStudioTilt] start called')
// #ifdef APP-PLUS
startNativeInternal()
// #endif

View File

@ -300,9 +300,9 @@
"current": 0,
"list": [
{
"name": "castlove-lenticular-create",
"path": "pages/castlove/create",
"query": "name=%E5%85%89%E6%A0%85%E5%8D%A1"
"name": "",
"path": "",
"query": ""
}
]
}

View File

@ -17,8 +17,6 @@
</view>
</view>
<!-- 加载中 -->
<view v-if="loading" class="loading-wrapper">
<!-- 旋转光环 -->
@ -1014,7 +1012,7 @@ onUnmounted(() => {
width: 352rpx;
height: 520rpx;
margin-bottom: 32rpx;
animation: card-3d-flip 15s ease-in-out infinite;
/* animation: card-3d-flip 15s ease-in-out infinite; */
transform-origin: center center;
}
@ -1036,18 +1034,18 @@ onUnmounted(() => {
}
}
.card-wrapper--lenticular {
/* .card-wrapper--lenticular {
width: 520rpx;
height: 680rpx;
}
} */
.detail-lenticular-slot {
position: absolute;
left: 50%;
top: 50%;
width: 78%;
width: 88%;
height: 96%;
border-radius: 64rpx;
border-radius: 48rpx;
/* transform: translate(-50%, -50%) rotate(-10deg); */
transform: translate(-50%, -50%) ;
z-index: 2;

View File

@ -10,9 +10,9 @@
</view>
<!-- <text class="nav-title">我的作品</text> -->
<view class="nav-placeholder"></view>
<!-- <view class="nav-settings" @tap="goToSettings">
<view class="nav-settings" @tap="goToSettings">
<image class="nav-settings-icon" src="/static/icon/settings.png" mode="aspectFit"></image>
</view> -->
</view>
</view>
<view class="scroll-content">
@ -31,7 +31,7 @@
<LenticularCard v-if="exhibitionAtSlot[0].is_lenticular" class="card-lenticular"
:layers="getLenticularLayers(exhibitionAtSlot[0].id)"
:transforms="getLenticularTransforms(exhibitionAtSlot[0].id)" :gyro-source="gyroSourceLabel"
:skip-built-in-touch="false" :shimmer-mid-opacity="0.16"
:skip-built-in-touch="true" :shimmer-mid-opacity="0.16"
@simulate="(x, y) => onLenticularSimulate(exhibitionAtSlot[0].id, x, y)" />
<image v-else class="card-image"
:src="exhibitionAtSlot[0].cover_url || '/static/nft/placeholder.png'" mode="aspectFill">
@ -76,7 +76,7 @@
<LenticularCard v-if="exhibitionAtSlot[1].is_lenticular" class="card-lenticular"
:layers="getLenticularLayers(exhibitionAtSlot[1].id)"
:transforms="getLenticularTransforms(exhibitionAtSlot[1].id)" :gyro-source="gyroSourceLabel"
:skip-built-in-touch="false" :shimmer-mid-opacity="0.16"
:skip-built-in-touch="true" :shimmer-mid-opacity="0.16"
@simulate="(x, y) => onLenticularSimulate(exhibitionAtSlot[1].id, x, y)" />
<image v-else class="card-image"
:src="exhibitionAtSlot[1].cover_url || '/static/nft/placeholder.png'" mode="aspectFill">
@ -154,7 +154,7 @@
<LenticularCard v-if="item.is_lenticular" class="liked-lenticular"
:layers="getLikedLenticularLayers(item.id)"
:transforms="getLikedLenticularTransforms(item.id)" :gyro-source="gyroSourceLabel"
:skip-built-in-touch="false" :shimmer-mid-opacity="0.16"
:skip-built-in-touch="true" :shimmer-mid-opacity="0.16"
@simulate="(x, y) => onLikedLenticularSimulate(item.id, x, y)" />
<image v-else class="liked-cover" :src="item.cover_url || '/static/nft/placeholder.png'"
mode="aspectFill"></image>