|
|
|
|
@ -8,9 +8,44 @@
|
|
|
|
|
<!-- 社区背景和OC活动区域 -->
|
|
|
|
|
<div class="community-scene">
|
|
|
|
|
<div class="scene-background">
|
|
|
|
|
<img src="/photo/map1.png" alt="社区背景" class="scene-image background-image" />
|
|
|
|
|
<div class="background-image-container"></div>
|
|
|
|
|
<!-- 天气叠加层:根据 weatherInfo.condition 动态添加样式 -->
|
|
|
|
|
<div v-if="weatherInfo" :class="['weather-overlay', weatherClass]"></div>
|
|
|
|
|
<!-- 添加:天气信息显示 -->
|
|
|
|
|
<div v-if="weatherInfo" class="weather-widget">
|
|
|
|
|
<div class="weather-content">
|
|
|
|
|
<div class="weather-details">
|
|
|
|
|
<!-- 添加地址输入框 -->
|
|
|
|
|
<div class="location-input">
|
|
|
|
|
<el-input
|
|
|
|
|
v-model="inputLocation"
|
|
|
|
|
placeholder="输入任意城市名称"
|
|
|
|
|
size="small"
|
|
|
|
|
@keyup.enter="updateWeather"
|
|
|
|
|
:disabled="weatherLoading"
|
|
|
|
|
>
|
|
|
|
|
<template #append>
|
|
|
|
|
<el-button
|
|
|
|
|
@click="updateWeather"
|
|
|
|
|
:icon="Search"
|
|
|
|
|
:loading="weatherLoading"
|
|
|
|
|
/>
|
|
|
|
|
</template>
|
|
|
|
|
</el-input>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="temperature">{{ weatherInfo.temperature }}°C</div>
|
|
|
|
|
<div class="condition">{{ weatherInfo.condition }}</div>
|
|
|
|
|
<div class="location">{{ weatherInfo.province }}{{ weatherInfo.city !== weatherInfo.province ? weatherInfo.city : '' }}</div>
|
|
|
|
|
<div class="weather-extras" v-if="weatherInfo?.humidity">
|
|
|
|
|
<span>湿度: {{ weatherInfo.humidity }}%</span>
|
|
|
|
|
<span>风速: {{ weatherInfo.windSpeed }}级</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 地点标记区域 - 修复:移到 oc-characters 外部 -->
|
|
|
|
|
<div class="location-pins">
|
|
|
|
|
<div
|
|
|
|
|
@ -109,6 +144,103 @@ import OCInfoDialog from './OCInfoDialog.vue'
|
|
|
|
|
import { ocList, loadOCFromStorage } from '../stores/ocStore.js'
|
|
|
|
|
import { currentUser } from '../stores/userStore.js'
|
|
|
|
|
|
|
|
|
|
const inputLocation = ref('天津')
|
|
|
|
|
const currentLocation = ref('天津')
|
|
|
|
|
|
|
|
|
|
//地图和天气函数
|
|
|
|
|
const getRealMap = async (location) => {
|
|
|
|
|
return {
|
|
|
|
|
imageUrl: '/photo/map1.png',
|
|
|
|
|
coordinates:
|
|
|
|
|
locationCoords
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//更新天气函数
|
|
|
|
|
const updateWeather = async () => {
|
|
|
|
|
if (inputLocation.value.trim()) {
|
|
|
|
|
weatherLoading.value = true
|
|
|
|
|
currentLocation.value = inputLocation.value.trim()
|
|
|
|
|
try {
|
|
|
|
|
const weatherData = await getWeatherData(currentLocation.value)
|
|
|
|
|
weatherInfo.value = weatherData
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('更新天气失败:', error)
|
|
|
|
|
} finally {
|
|
|
|
|
weatherLoading.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const getWeatherData = async (location) => {
|
|
|
|
|
try {
|
|
|
|
|
const API_KEY = '7d1e2dfdd86a45c64e4178c0ad321fcd'
|
|
|
|
|
|
|
|
|
|
// 第一步:通过地理编码API获取城市编码
|
|
|
|
|
const geoResponse = await fetch(`https://restapi.amap.com/v3/geocode/geo?address=${encodeURIComponent(location)}&key=${API_KEY}`)
|
|
|
|
|
const geoData = await geoResponse.json()
|
|
|
|
|
|
|
|
|
|
if (geoData.status !== '1' || !geoData.geocodes || geoData.geocodes.length === 0) {
|
|
|
|
|
throw new Error(`找不到地点: ${location}`)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取第一个匹配的城市编码
|
|
|
|
|
const adcode = geoData.geocodes[0].adcode
|
|
|
|
|
|
|
|
|
|
// 第二步:使用城市编码获取天气
|
|
|
|
|
const weatherResponse = await fetch(`https://restapi.amap.com/v3/weather/weatherInfo?city=${adcode}&key=${API_KEY}&extensions=base`)
|
|
|
|
|
const weatherData = await weatherResponse.json()
|
|
|
|
|
|
|
|
|
|
if (weatherData.status === '1' && weatherData.lives && weatherData.lives.length > 0) {
|
|
|
|
|
const live = weatherData.lives[0]
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
temperature: live.temperature,
|
|
|
|
|
condition: live.weather,
|
|
|
|
|
humidity: live.humidity,
|
|
|
|
|
windSpeed: live.windpower,
|
|
|
|
|
windDir: live.winddirection,
|
|
|
|
|
reportTime: live.reporttime,
|
|
|
|
|
province: live.province,
|
|
|
|
|
city: live.city
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error('获取天气数据失败')
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取真实天气失败:', error)
|
|
|
|
|
return getMockWeatherData(location)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 添加模拟数据作为后备函数
|
|
|
|
|
const getMockWeatherData = (location) => {
|
|
|
|
|
// 生成随机的天气数据
|
|
|
|
|
const conditions = ['晴', '多云', '阴', '小雨', '中雨', '大雨', '阵雨', '雷阵雨', '雪', '雾', '霾']
|
|
|
|
|
const randomCondition = conditions[Math.floor(Math.random() * conditions.length)]
|
|
|
|
|
|
|
|
|
|
// 根据季节生成合理温度
|
|
|
|
|
const now = new Date()
|
|
|
|
|
const month = now.getMonth() + 1
|
|
|
|
|
let baseTemp = 20
|
|
|
|
|
|
|
|
|
|
if (month >= 3 && month <= 5) baseTemp = 18 // 春季
|
|
|
|
|
else if (month >= 6 && month <= 8) baseTemp = 28 // 夏季
|
|
|
|
|
else if (month >= 9 && month <= 11) baseTemp = 15 // 秋季
|
|
|
|
|
else baseTemp = 5 // 冬季
|
|
|
|
|
|
|
|
|
|
const temperature = baseTemp + Math.floor(Math.random() * 15) - 5
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
temperature: temperature,
|
|
|
|
|
condition: randomCondition,
|
|
|
|
|
humidity: 30 + Math.floor(Math.random() * 50),
|
|
|
|
|
windSpeed: (1 + Math.random() * 6).toFixed(1),
|
|
|
|
|
province: '未知省份',
|
|
|
|
|
city: location
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 默认头像
|
|
|
|
|
const defaultAvatar = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgdmlld0JveD0iMCAwIDEwMCAxMDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIiBmaWxsPSIjZGRlMGU2Ii8+CjxjaXJjbGUgY3g9IjUwIiBjeT0iMzUiIHI9IjIwIiBmaWxsPSIjYThiOWNlIi8+CjxwYXRoIGQ9Ik0wIDg1QzAgNzAuNjQgMTEuNjQgNTkgMjYgNTlINzRDODguMzYgNTkgMTAwIDcwLjY0IDEwMCA4NVYxMDBIMFY4NVoiIGZpbGw9IiNhOGI5Y2UiLz4KPC9zdmc+'
|
|
|
|
|
|
|
|
|
|
@ -127,6 +259,22 @@ const locationCoords = {
|
|
|
|
|
'宠物店': { x: 35, y: 75 }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const currentMap = ref('')
|
|
|
|
|
const weatherInfo = ref(null)
|
|
|
|
|
const weatherLoading = ref(false)
|
|
|
|
|
|
|
|
|
|
// 根据 weatherInfo.condition 映射到 CSS 类名
|
|
|
|
|
const weatherClass = computed(() => {
|
|
|
|
|
if (!weatherInfo.value || !weatherInfo.value.condition) return ''
|
|
|
|
|
const c = String(weatherInfo.value.condition).toLowerCase()
|
|
|
|
|
if (/晴|clear|sunny/.test(c)) return 'wb-sunny'
|
|
|
|
|
if (/云|clouds|cloud/.test(c)) return 'wb-cloudy'
|
|
|
|
|
if (/雨|drizzle|rain|雷|thunder/.test(c)) return 'wb-rain'
|
|
|
|
|
if (/雪|snow/.test(c)) return 'wb-snow'
|
|
|
|
|
if (/雾|霾|fog|haze|mist|smoke|dust/.test(c)) return 'wb-haze'
|
|
|
|
|
return 'wb-default'
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 计算OC当前位置(基于其 schedule)
|
|
|
|
|
const ocPositions = ref([])
|
|
|
|
|
// 每个地点的实时状态(位置、在场人数、对应 oc 列表)
|
|
|
|
|
@ -302,6 +450,7 @@ onMounted(() => {
|
|
|
|
|
computePositions()
|
|
|
|
|
posTimer = setInterval(computePositions, 60 * 1000)
|
|
|
|
|
document.addEventListener('click', closePopoverOnClickOutside)
|
|
|
|
|
initMapAndWeather()
|
|
|
|
|
})
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
if (posTimer) clearInterval(posTimer)
|
|
|
|
|
@ -376,6 +525,27 @@ const closePopoverOnClickOutside = (event) => {
|
|
|
|
|
closeLocationPopover()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const initMapAndWeather = async () => {
|
|
|
|
|
try {
|
|
|
|
|
// 获取地图(始终使用本地图片)
|
|
|
|
|
const mapData = await getRealMap(currentLocation.value)
|
|
|
|
|
currentMap
|
|
|
|
|
.value = mapData.
|
|
|
|
|
imageUrl
|
|
|
|
|
|
|
|
|
|
// 获取天气信息
|
|
|
|
|
const weatherData = await getWeatherData(currentLocation.value)
|
|
|
|
|
weatherInfo
|
|
|
|
|
.value =
|
|
|
|
|
weatherData
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console
|
|
|
|
|
.error('初始化天气失败:', error)
|
|
|
|
|
currentMap
|
|
|
|
|
.value = '/photo/map1.png'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
@ -414,18 +584,34 @@ const closePopoverOnClickOutside = (event) => {
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
|
|
|
|
background: #f0f0f0;
|
|
|
|
|
/* 强制基于视口计算高度,不受父元素padding影响 */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.scene-background {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
position: relative;
|
|
|
|
|
background: #f0f0f0; /* 加载前的背景色 */
|
|
|
|
|
overflow: visible; /* 改为visible避免裁剪 */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.scene-image {
|
|
|
|
|
.background-image-container {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
/* 使用背景图替代img,利用background-size控制显示方式 */
|
|
|
|
|
background-image: url("/photo/map1.png");
|
|
|
|
|
background-repeat: no-repeat;
|
|
|
|
|
background-position: center; /* 图片居中显示 */
|
|
|
|
|
background-size: contain; /* 完整显示图片,可能有留白 */
|
|
|
|
|
/* 若希望图片铺满容器(允许裁剪边缘),可替换为:
|
|
|
|
|
background-size: cover;
|
|
|
|
|
*/
|
|
|
|
|
z-index: 1; /* 确保在更底层,不遮挡其他内容 */
|
|
|
|
|
pointer-events: none; /* 禁止背景图片拦截鼠标事件,保障天气卡等可交互 */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.right-panel {
|
|
|
|
|
@ -473,6 +659,7 @@ const closePopoverOnClickOutside = (event) => {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
z-index: 10; /* 高于背景图 */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.location-pin {
|
|
|
|
|
@ -730,4 +917,216 @@ const closePopoverOnClickOutside = (event) => {
|
|
|
|
|
padding: 3px 5px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* 响应式调整 */
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
|
.weather-widget {
|
|
|
|
|
top: 10px;
|
|
|
|
|
right: 10px;
|
|
|
|
|
padding: 8px;
|
|
|
|
|
min-width: 110px;
|
|
|
|
|
max-width: 130px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.temperature {
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.condition, .location {
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.weather-extras {
|
|
|
|
|
font-size: 8px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
|
.community-scene {
|
|
|
|
|
height: calc(100vh - 150px); /* 小屏幕下根据布局调整高度,适配更多空间 */
|
|
|
|
|
}
|
|
|
|
|
.scene-image {
|
|
|
|
|
object-fit: contain; /* 小屏幕下优先保证图片完整显示 */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* 天气组件样式 */
|
|
|
|
|
.weather-widget {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0px;
|
|
|
|
|
right: 0px;
|
|
|
|
|
background: rgba(255, 255, 255, 0.95);
|
|
|
|
|
backdrop-filter: blur(10px);
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
padding: 10px; /* 减少内边距 */
|
|
|
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|
|
|
|
z-index: 30;
|
|
|
|
|
min-width: 50px; /* 减小最小宽度 */
|
|
|
|
|
max-width: 120px; /* 添加最大宽度限制 */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.weather-content {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 6px; /* 减小间距 */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.weather-details {
|
|
|
|
|
color: #333;
|
|
|
|
|
text-align: center;
|
|
|
|
|
font-size: 0.9em; /* 整体缩小字体 */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.location-input {
|
|
|
|
|
margin-bottom: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.location-input :deep(.el-input-group) {
|
|
|
|
|
background: rgba(255,255,255,0.9);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
border: 1px solid #e4e7ed;
|
|
|
|
|
height: 28px; /* 减小高度 */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.location-input :deep(.el-input__inner) {
|
|
|
|
|
border: none;
|
|
|
|
|
background: transparent;
|
|
|
|
|
padding: 0 8px;
|
|
|
|
|
font-size: 11px; /* 减小字体 */
|
|
|
|
|
height: 26px;
|
|
|
|
|
line-height: 26px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.location-input :deep(.el-input-group__append) {
|
|
|
|
|
padding: 0 6px;
|
|
|
|
|
background: transparent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.location-input :deep(.el-button) {
|
|
|
|
|
padding: 6px;
|
|
|
|
|
height: auto;
|
|
|
|
|
}
|
|
|
|
|
.temperature {
|
|
|
|
|
font-size: 16px; /* 减小温度字体 */
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
margin-bottom: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.condition {
|
|
|
|
|
font-size: 11px; /* 减小天气状况字体 */
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
margin-bottom: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.location {
|
|
|
|
|
font-size: 10px; /* 减小地点字体 */
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.weather-extras {
|
|
|
|
|
font-size: 9px; /* 减小额外信息字体 */
|
|
|
|
|
opacity: 0.7;
|
|
|
|
|
margin-top: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.weather-extras span {
|
|
|
|
|
display: block;
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 天气覆盖层样式 */
|
|
|
|
|
.weather-overlay {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
z-index: 2; /* 位于背景(map)之上、地点图标之下(地点图标 z-index:10) */
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
transition: opacity 0.6s ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 晴天:暖色渐变 + 太阳光环 */
|
|
|
|
|
.weather-overlay.wb-sunny {
|
|
|
|
|
background: linear-gradient(180deg, rgba(255,240,160,0.12), rgba(255,210,120,0.05));
|
|
|
|
|
}
|
|
|
|
|
.weather-overlay.wb-sunny::before {
|
|
|
|
|
content: "";
|
|
|
|
|
position: absolute;
|
|
|
|
|
right: 8%;
|
|
|
|
|
top: 8%;
|
|
|
|
|
width: 160px;
|
|
|
|
|
height: 160px;
|
|
|
|
|
background: radial-gradient(circle at 30% 30%, rgba(255,245,200,0.9) 0%, rgba(255,220,120,0.6) 30%, rgba(255,200,80,0.15) 60%, transparent 70%);
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
filter: blur(12px);
|
|
|
|
|
transform: translateZ(0);
|
|
|
|
|
animation: sunSlowRotate 12s linear infinite;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes sunSlowRotate {
|
|
|
|
|
from { transform: rotate(0deg); }
|
|
|
|
|
to { transform: rotate(360deg); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 多云:灰白云层覆盖(使用伪元素叠加多个模糊圆形) */
|
|
|
|
|
.weather-overlay.wb-cloudy {
|
|
|
|
|
background: linear-gradient(180deg, rgba(200,210,220,0.06), rgba(160,170,180,0.06));
|
|
|
|
|
}
|
|
|
|
|
.weather-overlay.wb-cloudy::before,
|
|
|
|
|
.weather-overlay.wb-cloudy::after {
|
|
|
|
|
content: "";
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: 10%;
|
|
|
|
|
top: 5%;
|
|
|
|
|
width: 420px;
|
|
|
|
|
height: 120px;
|
|
|
|
|
background: radial-gradient(ellipse at center, rgba(255,255,255,0.85) 0%, rgba(240,240,240,0.9) 35%, rgba(220,220,220,0.95) 60%, rgba(200,200,200,0.95) 100%);
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
filter: blur(18px);
|
|
|
|
|
transform: translateX(0);
|
|
|
|
|
animation: cloudFloat 18s linear infinite;
|
|
|
|
|
}
|
|
|
|
|
.weather-overlay.wb-cloudy::after {
|
|
|
|
|
left: 48%;
|
|
|
|
|
top: 18%;
|
|
|
|
|
width: 360px;
|
|
|
|
|
height: 110px;
|
|
|
|
|
animation-duration: 22s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes cloudFloat {
|
|
|
|
|
0% { transform: translateX(-6%); }
|
|
|
|
|
50% { transform: translateX(6%); }
|
|
|
|
|
100% { transform: translateX(-6%); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 雨天:深色背景 + 动态雨线(用重复渐变模拟雨丝) */
|
|
|
|
|
.weather-overlay.wb-rain {
|
|
|
|
|
background: linear-gradient(180deg, rgba(30,40,50,0.18), rgba(20,25,30,0.2));
|
|
|
|
|
}
|
|
|
|
|
.weather-overlay.wb-rain::before {
|
|
|
|
|
content: "";
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
background-image: linear-gradient(transparent 70%, rgba(255,255,255,0.06) 70%);
|
|
|
|
|
background-size: 4px 40px;
|
|
|
|
|
opacity: 0.9;
|
|
|
|
|
animation: rainFall 0.8s linear infinite;
|
|
|
|
|
mix-blend-mode: screen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes rainFall {
|
|
|
|
|
from { background-position: 0 -10px; }
|
|
|
|
|
to { background-position: 0 40px; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 霾/雾:浅灰覆盖 + 模糊 */
|
|
|
|
|
.weather-overlay.wb-haze {
|
|
|
|
|
background: rgba(200,200,200,0.25);
|
|
|
|
|
backdrop-filter: blur(4px) saturate(0.9);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 默认轻微蒙层 */
|
|
|
|
|
.weather-overlay.wb-default {
|
|
|
|
|
background: rgba(0,0,0,0.02);
|
|
|
|
|
}
|
|
|
|
|
</style>
|