143 lines
3.3 KiB
Vue
143 lines
3.3 KiB
Vue
<template>
|
|
<view
|
|
class="food-tile-icon"
|
|
:class="[`food-tile-icon--${size}`, { 'is-custom-img': hasCustomImg }]"
|
|
>
|
|
<text v-if="showEmojiFallback" class="food-tile-icon__emoji">{{ fallbackIcon }}</text>
|
|
<image
|
|
v-if="src && !imageFailed"
|
|
class="food-tile-icon__img"
|
|
:src="src"
|
|
:mode="imageMode"
|
|
@error="onImageError"
|
|
/>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed, ref, watch } from 'vue'
|
|
import { getFoodImgUrl } from '../data/gameFoodImg.js'
|
|
|
|
const props = defineProps({
|
|
food: { type: [Object, String], default: null },
|
|
size: { type: String, default: 'tile' }
|
|
})
|
|
|
|
const imageFailed = ref(false)
|
|
|
|
const fallbackIcon = computed(() => {
|
|
if (typeof props.food === 'object' && props.food?.icon) return props.food.icon
|
|
return '🍽'
|
|
})
|
|
|
|
const hasCustomImg = computed(() => typeof props.food === 'object' && !!props.food?.img)
|
|
|
|
const src = computed(() => {
|
|
if (typeof props.food === 'object' && props.food?.img) return props.food.img
|
|
if (typeof props.food === 'object' && props.food?.icon) return getFoodImgUrl(props.food.icon)
|
|
return ''
|
|
})
|
|
|
|
/** 有可用图片时不渲染 emoji,避免 PNG 透明区域露出底层符号 */
|
|
const showEmojiFallback = computed(() => !src.value || imageFailed.value)
|
|
|
|
/** 自定义素材用 aspectFill 铺满,减少边缘露底 */
|
|
const imageMode = computed(() => (hasCustomImg.value ? 'aspectFill' : 'aspectFit'))
|
|
|
|
watch(
|
|
() => src.value,
|
|
() => {
|
|
imageFailed.value = false
|
|
}
|
|
)
|
|
|
|
function onImageError() {
|
|
imageFailed.value = true
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.food-tile-icon {
|
|
position: relative;
|
|
box-sizing: border-box;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.food-tile-icon--tile {
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
font-size: calc(var(--tile-emoji-size, 48px) * 0.6);
|
|
}
|
|
|
|
.food-tile-icon--tile .food-tile-icon__emoji,
|
|
.food-tile-icon--tile .food-tile-icon__img {
|
|
left: 50%;
|
|
top: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 75%;
|
|
height: 75%;
|
|
z-index: 2;
|
|
}
|
|
|
|
.food-tile-icon--tile.is-custom-img .food-tile-icon__img {
|
|
width: 82%;
|
|
height: 82%;
|
|
}
|
|
|
|
/* 玻璃格上的食材图片:投影增强立体感(参考稿) */
|
|
.food-tile-icon--tile .food-tile-icon__img {
|
|
filter: drop-shadow(0 6rpx 10rpx rgba(0, 0, 0, 0.16));
|
|
}
|
|
|
|
.food-tile-icon--goal {
|
|
width: 40rpx;
|
|
height: 40rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.food-tile-icon--tooltip {
|
|
width: 36rpx;
|
|
height: 36rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.food-tile-icon__emoji {
|
|
position: absolute;
|
|
left: 50%;
|
|
top: 50%;
|
|
transform: translate(-50%, -50%);
|
|
font-size: inherit;
|
|
line-height: 1;
|
|
z-index: 1;
|
|
}
|
|
|
|
.food-tile-icon--goal .food-tile-icon__emoji,
|
|
.food-tile-icon--tooltip .food-tile-icon__emoji {
|
|
position: static;
|
|
transform: none;
|
|
font-size: 34rpx;
|
|
}
|
|
|
|
.food-tile-icon--goal.is-custom-img .food-tile-icon__img,
|
|
.food-tile-icon--tooltip.is-custom-img .food-tile-icon__img {
|
|
border-radius: 8rpx;
|
|
}
|
|
|
|
.food-tile-icon__img {
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
z-index: 2;
|
|
display: block;
|
|
}
|
|
</style>
|