Support calendar

This commit is contained in:
omg-xtao 2023-02-16 16:47:48 +08:00 committed by GitHub
parent 1835eae244
commit ea94583a5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 2755 additions and 8 deletions

View File

@ -457,8 +457,18 @@ class _NamecardAssets(_AssetsService):
def game_name(self) -> str: def game_name(self) -> str:
return NAMECARD_DATA[str(self.id)]["icon"] return NAMECARD_DATA[str(self.id)]["icon"]
@lru_cache
def _get_id_from_avatar_id(self, avatar_id: Union[int, str]) -> int:
avatar_icon_name = AVATAR_DATA[str(avatar_id)]["icon"].replace("AvatarIcon", "NameCardIcon")
for namecard_id, namecard_data in NAMECARD_DATA.items():
if namecard_data["icon"] == avatar_icon_name:
return int(namecard_id)
raise ValueError(avatar_id)
def __call__(self, target: int) -> "_NamecardAssets": def __call__(self, target: int) -> "_NamecardAssets":
result = _NamecardAssets(self.client) result = _NamecardAssets(self.client)
if target > 10000000:
target = self._get_id_from_avatar_id(target)
result.id = target result.id = target
result.enka = DEFAULT_EnkaAssets.namecards(target) result.enka = DEFAULT_EnkaAssets.namecards(target)
return result return result

View File

@ -9,7 +9,7 @@ from httpx import AsyncClient, RemoteProtocolError, Response, URL
from utils.const import AMBR_HOST, PROJECT_ROOT from utils.const import AMBR_HOST, PROJECT_ROOT
from utils.log import logger from utils.log import logger
__all__ = ["update_metadata_from_ambr", "update_metadata_from_github", "make_github_fast"] __all__ = ["update_metadata_from_ambr", "update_metadata_from_github", "make_github_fast", "RESOURCE_DEFAULT_PATH"]
GENSHIN_PY_DATA_REPO = parse_token("aHR0cHM6Ly9naXRsYWIuY29tL0RpbWJyZWF0aC9nYW1lZGF0YS8tL3Jhdy9tYXN0ZXIv").decode() GENSHIN_PY_DATA_REPO = parse_token("aHR0cHM6Ly9naXRsYWIuY29tL0RpbWJyZWF0aC9nYW1lZGF0YS8tL3Jhdy9tYXN0ZXIv").decode()
RESOURCE_REPO = "PaiGramTeam/PaiGram_Resources" RESOURCE_REPO = "PaiGramTeam/PaiGram_Resources"
RESOURCE_BRANCH = "remote" RESOURCE_BRANCH = "remote"

View File

@ -0,0 +1,359 @@
import re
from datetime import datetime, timedelta
from typing import List, Tuple, Optional, Dict, Union
from httpx import AsyncClient
from core.base.assets import AssetsService
from metadata.genshin import AVATAR_DATA
from metadata.scripts.metadatas import RESOURCE_DEFAULT_PATH
from metadata.shortname import roleToId
from modules.apihelper.models.genshin.calendar import Date, FinalAct, ActEnum, ActDetail, ActTime, BirthChar
from modules.wiki.character import Character
class Calendar:
"""原神活动日历"""
ANNOUNCEMENT_LIST = "https://hk4e-api.mihoyo.com/common/hk4e_cn/announcement/api/getAnnList"
ANNOUNCEMENT_PARAMS = {
"game": "hk4e",
"game_biz": "hk4e_cn",
"lang": "zh-cn",
"bundle_id": "hk4e_cn",
"platform": "pc",
"region": "cn_gf01",
"level": "55",
"uid": "100000000",
}
MIAO_API = "http://miaoapi.cn/api/calendar"
REMOTE_API = f"https://raw.fastgit.org/{RESOURCE_DEFAULT_PATH}calendar.json"
IGNORE_IDS = [
495, # 有奖问卷调查开启!
1263, # 米游社《原神》专属工具一览
423, # 《原神》玩家社区一览
422, # 《原神》防沉迷系统说明
762, # 《原神》公平运营声明
762, # 《原神》公平运营声明
]
IGNORE_RE = re.compile(r"(内容专题页|版本更新说明|调研|防沉迷|米游社|专项意见|更新修复与优化|问卷调查|版本更新通知|更新时间说明|预下载功能|周边限时|周边上新|角色演示)")
FULL_TIME_RE = re.compile(r"(魔神任务)")
def __init__(self):
self.client = AsyncClient()
self.birthday_list = self.gen_birthday_list()
@staticmethod
def gen_birthday_list() -> Dict[str, List[str]]:
"""生成生日列表"""
birthday_list = {}
for value in AVATAR_DATA.values():
key = "_".join([str(i) for i in value["birthday"]])
data = birthday_list.get(key, [])
data.append(value["name"])
birthday_list[key] = data
return birthday_list
@staticmethod
def get_now_hour() -> datetime:
"""获取当前时间"""
return datetime.now().replace(minute=0, second=0, microsecond=0)
async def req_cal_data(self) -> Tuple[List[List[ActDetail]], Dict[str, ActTime]]:
"""请求日历数据"""
list_data = await self.client.get(self.ANNOUNCEMENT_LIST, params=self.ANNOUNCEMENT_PARAMS)
list_data = list_data.json()
new_list_data = [[], []]
for idx, data in enumerate(list_data.get("data", {}).get("list", [])):
for item in data.get("list", []):
new_list_data[idx].append(ActDetail(**item))
time_map = {}
req = await self.client.get(self.MIAO_API)
if req.status_code == 200:
miao_data = req.json()
time_map.update({key: ActTime(**value) for key, value in miao_data.get("data", {}).items()})
req = await self.client.get(self.REMOTE_API)
if req.status_code == 200:
remote_data = req.json()
time_map.update({key: ActTime(**value) for key, value in remote_data.get("data", {}).items()})
return new_list_data, time_map
@staticmethod
def date_to_weekday(date_: datetime) -> str:
"""日期转换为星期"""
time = ["", "", "", "", "", "", ""]
return time[date_.weekday()]
async def get_date_list(self) -> Tuple[List[Date], datetime, datetime, timedelta, float]:
"""获取日历数据"""
data_list: List[Date] = []
today = self.get_now_hour()
temp = today - timedelta(days=7)
month = 0
date, week, is_today = [], [], []
start_date, end_date = None, None
for i in range(13):
temp += timedelta(days=1)
m, d, w = temp.month, temp.day, self.date_to_weekday(temp)
if month == 0:
start_date = temp
month = m
if month != m and len(date) > 0:
data_list.append(Date(month=month, date=date, week=week, is_today=is_today))
date, week, is_today = [], [], []
month = m
date.append(d)
week.append(w)
is_today.append(temp == today)
if i == 12:
data_list.append(Date(month=month, date=date, week=week, is_today=is_today))
end_date = temp
start_time = start_date.replace(hour=0, minute=0, second=0, microsecond=0)
end_time = end_date.replace(hour=23, minute=59, second=59, microsecond=999999)
total_range: timedelta = end_time - start_time
now_left: float = (self.get_now_hour() - start_time) / total_range * 100
return (
data_list,
start_time,
end_time,
total_range,
now_left,
)
@staticmethod
def human_read(d: timedelta) -> str:
"""将日期转换为人类可读"""
hour = d.seconds // 3600
minute = d.seconds // 60 % 60
if minute >= 59:
hour += 1
text = ""
if d.days:
text += f"{d.days}"
if hour:
text += f"{hour}小时"
return text
@staticmethod
def count_width(
act: FinalAct,
detail: Optional[ActTime],
ds: ActDetail,
start_time: datetime,
end_time: datetime,
total_range: timedelta,
) -> Tuple[datetime, datetime]:
"""计算宽度"""
def get_date(d1: str, d2: str) -> datetime:
if d1 and len(d1) > 6:
return datetime.strptime(d1, "%Y-%m-%d %H:%M:%S")
return datetime.strptime(d2, "%Y-%m-%d %H:%M:%S")
s_date = get_date(detail and detail.start, ds.start_time)
e_date = get_date(detail and detail.end, ds.end_time)
s_time = max(s_date, start_time)
e_time = min(e_date, end_time)
s_range = s_time - start_time
e_range = e_time - start_time
act.left = s_range / total_range * 100
act.width = e_range / total_range * 100 - act.left
act.duration = (e_time - s_time).total_seconds()
act.start = s_date.strftime("%m-%d %H:%M")
act.end = e_date.strftime("%m-%d %H:%M")
return s_date, e_date
def parse_label(self, act: FinalAct, is_act: bool, s_date: datetime, e_date: datetime) -> None:
"""解析活动标签"""
now = self.get_now_hour()
label = ""
if self.FULL_TIME_RE.findall(act.title) or e_date - s_date > timedelta(days=365):
label = f"{s_date.strftime('%m-%d %H:%M')} 后永久有效" if s_date < now else "永久有效"
elif s_date < now < e_date:
label = f'{e_date.strftime("%m-%d %H:%M")} ({self.human_read(e_date - now)}后结束)'
if act.width > (38 if is_act else 55):
label = f"{s_date.strftime('%m-%d %H:%M')} ~ {label}"
elif s_date > now:
label = f'{s_date.strftime("%m-%d %H:%M")} ({self.human_read(s_date - now)}后开始)'
elif is_act:
label = f"{s_date.strftime('%m-%d %H:%M')} ~ {e_date.strftime('%m-%d %H:%M')}"
act.label = label
@staticmethod
async def parse_type(act: FinalAct, assets: AssetsService) -> None:
"""解析活动类型"""
if "神铸赋形" in act.title:
act.type = ActEnum.weapon
act.title = re.sub(r"(单手剑|双手剑|长柄武器|弓|法器|·)", "", act.title)
act.sort = 2
elif "祈愿" in act.title:
act.type = ActEnum.character
if reg_ret := re.search(r"·(.*)\(", act.title):
char_name = reg_ret[1]
char = assets.avatar(roleToId(char_name))
act.banner = (await assets.namecard(char.id).navbar()).as_uri()
act.face = (await char.icon()).as_uri()
act.sort = 1
elif "纪行" in act.title:
act.type = ActEnum.no_display
elif act.title == "深渊":
act.type = ActEnum.abyss
async def get_list(
self,
ds: ActDetail,
start_time: datetime,
end_time: datetime,
total_range: timedelta,
time_map: Dict[str, ActTime],
is_act: bool,
assets: AssetsService,
) -> Optional[FinalAct]:
"""获取活动列表"""
act = FinalAct(
id=ds.ann_id,
type=ActEnum.activity if is_act else ActEnum.normal,
title=ds.title,
banner=ds.banner if is_act else "",
sort=5 if is_act else 10,
icon=ds.tag_icon,
)
detail: Optional[ActTime] = time_map.get(str(act.id))
if act.id in self.IGNORE_IDS or self.IGNORE_RE.findall(act.title) or (detail and not detail.display):
return None
await self.parse_type(act, assets)
s_date, e_date = self.count_width(act, detail, ds, start_time, end_time, total_range)
self.parse_label(act, is_act, s_date, e_date)
if s_date <= end_time and e_date >= start_time:
act.mergeStatus = 1 if act.type in {ActEnum.activity, ActEnum.normal} else 0
return act
@staticmethod
def get_abyss_cal(start_time: datetime, end_time: datetime) -> List[List[Union[datetime, str]]]:
"""获取深渊日历"""
last = datetime.now().replace(day=1) - timedelta(days=2)
last_month = last.month
curr = datetime.now()
curr_month = curr.month
next_date = last + timedelta(days=40)
next_month = next_date.month
def start(date: datetime, up: bool = False):
return date.replace(day=1 if up else 16, hour=4, minute=0, second=0, microsecond=0)
def end(date: datetime, up: bool = False):
return date.replace(day=1 if up else 16, hour=3, minute=59, second=59, microsecond=999999)
check = [
[start(last, False), end(last, True), f"{last_month}月下半"],
[start(curr, True), end(curr, False), f"{curr_month}月上半"],
[start(curr, False), end(next_date, True), f"{curr_month}月下半"],
[start(next_date, True), end(next_date, False), f"{next_month}月上半"],
]
ret = []
for ds in check:
s, e, _ = ds
if (s <= start_time <= e) or (s <= end_time <= e):
ret.append(ds)
return ret
async def get_birthday_char(
self, date_list: List[Date], assets: AssetsService
) -> Tuple[int, Dict[str, Dict[str, List[BirthChar]]]]:
"""获取生日角色"""
birthday_char_line = 0
birthday_chars = {}
for date in date_list:
birthday_chars[str(date.month)] = {}
for d in date.date:
key = f"{date.month}_{d}"
if char := self.birthday_list.get(key):
birthday_char_line = max(len(char), birthday_char_line)
birthday_chars[str(date.month)][str(d)] = []
for c in char:
character = await Character.get_by_name(c)
birthday_chars[str(date.month)][str(d)].append(
BirthChar(
name=c,
star=character.rarity,
icon=(await assets.avatar(roleToId(c)).icon()).as_uri(),
)
)
return birthday_char_line, birthday_chars
@staticmethod
def get_merge_next(target: List[FinalAct], li: FinalAct) -> Optional[FinalAct]:
"""获取下一个可以合并的活动"""
return next(
(li2 for li2 in target if (li2.mergeStatus == 1) and (li.left + li.width <= li2.left)),
None,
)
def merge_list(self, target: List[FinalAct]) -> Tuple[List[List[FinalAct]], int, int]:
"""将两个活动合并为一行"""
char_count = 0
char_old = 0
ret: List[List[FinalAct]] = []
for idx, li in enumerate(target):
if li.type == ActEnum.character:
char_count += 1
if li.left == 0:
char_old += 1
li.idx = char_count
if li.mergeStatus == 1:
if li2 := self.get_merge_next(target[idx + 1 :], li):
li.mergeStatus = 2
li2.mergeStatus = 2
ret.append([li, li2])
if li.mergeStatus != 2:
li.mergeStatus = 2
ret.append([li])
return ret, char_count, char_old
async def get_photo_data(self, assets: AssetsService) -> Dict:
"""获取数据"""
now = self.get_now_hour()
list_data, time_map = await self.req_cal_data()
(
date_list,
start_time,
end_time,
total_range,
now_left,
) = await self.get_date_list()
birthday_char_line, birthday_chars = await self.get_birthday_char(date_list, assets)
target: List[FinalAct] = []
abyss: List[FinalAct] = []
for ds in list_data[1]:
if act := await self.get_list(ds, start_time, end_time, total_range, time_map, True, assets):
target.append(act)
for ds in list_data[0]:
if act := await self.get_list(ds, start_time, end_time, total_range, time_map, False, assets):
target.append(act)
abyss_cal = self.get_abyss_cal(start_time, end_time)
for t in abyss_cal:
ds = ActDetail(
title=f"「深境螺旋」· {t[2]}",
start_time=t[0].strftime("%Y-%m-%d %H:%M:%S"),
end_time=t[1].strftime("%Y-%m-%d %H:%M:%S"),
)
if act := await self.get_list(ds, start_time, end_time, total_range, {}, True, assets):
abyss.append(act)
target.sort(key=lambda x: (x.sort, x.start, x.duration))
target, char_count, char_old = self.merge_list(target)
return {
"date_list": date_list,
"now_left": now_left,
"list": target,
"abyss": abyss,
"char_mode": f"char-{char_count}-{char_old}",
"now_time": now.strftime("%Y-%m-%d %H 时"),
"birthday_char_line": birthday_char_line,
"birthday_chars": birthday_chars,
}

View File

@ -0,0 +1,76 @@
from enum import Enum
from typing import List
from pydantic import BaseModel
class Date(BaseModel):
"""日历日期"""
month: int
date: List[int]
week: List[str]
is_today: List[bool]
class ActEnum(str, Enum):
"""活动类型"""
character = "character"
weapon = "weapon"
activity = "activity"
normal = "normal"
no_display = "pass"
abyss = "abyss"
def __str__(self) -> str:
return self.value
class FinalAct(BaseModel):
"""最终活动数据"""
id: int
type: ActEnum
title: str
banner: str
mergeStatus: int = 0
face: str = ""
icon: str = ""
left: float = 0.0
width: float = 0.0
label: str = ""
sort: int = 0
idx: int = 0
start: str = ""
end: str = ""
duration: int = 0
class ActDetail(BaseModel):
"""活动详情"""
ann_id: int = 0
banner: str = ""
tag_icon: str = ""
title: str
start_time: str
end_time: str
...
class ActTime(BaseModel):
"""活动时间"""
title: str = ""
start: str = ""
end: str = ""
display: bool = True
class BirthChar(BaseModel):
"""生日角色"""
name: str
star: int
icon: str

View File

@ -19,6 +19,7 @@ from core.user import UserService
from core.user.error import UserNotFoundError from core.user.error import UserNotFoundError
from metadata.genshin import AVATAR_DATA from metadata.genshin import AVATAR_DATA
from metadata.shortname import roleToId, roleToName from metadata.shortname import roleToId, roleToName
from modules.apihelper.client.components.calendar import Calendar
from utils.bot import get_args from utils.bot import get_args
from utils.decorators.error import error_callable from utils.decorators.error import error_callable
from utils.decorators.restricts import restricts from utils.decorators.restricts import restricts
@ -48,12 +49,7 @@ class BirthdayPlugin(Plugin, BasePlugin):
cookie_service: CookiesService = None, cookie_service: CookiesService = None,
): ):
"""Load Data.""" """Load Data."""
self.birthday_list = {} self.birthday_list = Calendar.gen_birthday_list()
for value in AVATAR_DATA.values():
key = "_".join([str(i) for i in value["birthday"]])
data = self.birthday_list.get(key, [])
data.append(value["name"])
self.birthday_list.update({key: data})
self.user_service = user_service self.user_service = user_service
self.cookie_service = cookie_service self.cookie_service = cookie_service

View File

@ -0,0 +1,62 @@
from typing import Dict
from telegram import Update
from telegram.constants import ChatAction
from telegram.ext import CallbackContext, MessageHandler, filters
from core.base.assets import AssetsService
from core.base.redisdb import RedisDB
from core.baseplugin import BasePlugin
from core.plugin import Plugin, handler
from core.template import TemplateService
from modules.apihelper.client.components.calendar import Calendar
from utils.decorators.error import error_callable
from utils.decorators.restricts import restricts
from utils.log import logger
try:
import ujson as jsonlib
except ImportError:
import json as jsonlib
class CalendarPlugin(Plugin, BasePlugin):
"""活动日历查询"""
def __init__(
self,
template_service: TemplateService = None,
assets_service: AssetsService = None,
redis: RedisDB = None,
):
self.template_service = template_service
self.assets_service = assets_service
self.calendar = Calendar()
self.cache = redis.client
async def _fetch_data(self) -> Dict:
if data := await self.cache.get("plugin:calendar"):
return jsonlib.loads(data.decode("utf-8"))
data = await self.calendar.get_photo_data(self.assets_service)
await self.cache.set("plugin:calendar", jsonlib.dumps(data, default=lambda x: x.dict()), ex=1800)
return data
@handler.command("calendar", block=False)
@handler(MessageHandler, filters=filters.Regex(r"^(活动)+(日历|日历列表)$"), block=False)
@restricts()
@error_callable
async def command_start(self, update: Update, _: CallbackContext) -> None:
user = update.effective_user
message = update.effective_message
mode = "list" if "列表" in message.text else "calendar"
logger.info("用户 %s[%s] 查询日历 | 模式 %s", user.full_name, user.id, mode)
await message.reply_chat_action(ChatAction.TYPING)
data = await self._fetch_data()
data["display_mode"] = mode
image = await self.template_service.render(
"genshin/calendar/calendar.html",
data,
query_selector=".container",
)
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
await image.reply_photo(message)

View File

@ -146,9 +146,16 @@
<div class="command-description">角色生日</div> <div class="command-description">角色生日</div>
</div> </div>
<div class="command"> <div class="command">
<div class="command-name">/birthday_card</div> <div class="command-name">
/birthday_card
<i class="fa fa-id-card-o ml-2"></i>
</div>
<div class="command-description">领取角色生日画片</div> <div class="command-description">领取角色生日画片</div>
</div> </div>
<div class="command">
<div class="command-name">/calendar</div>
<div class="command-description">活动日历</div>
</div>
</div> </div>
</div> </div>

View File

@ -0,0 +1,450 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
}
body {
font-size: 18px;
color: #1e1f20;
transform: scale(1);
transform-origin: 0 0;
width: 996px;
}
.container {
width: 996px;
padding: 10px 0px 10px 0px;
background-size: 100% 100%;
}
.logo {
font-size: 18px;
text-align: center;
color: #fff;
margin: 20px 0 10px 0;
}
.calendar {
min-height: 400px;
position: relative;
padding: 1px 0;
width: 956px;
margin: 20px;
box-shadow: 0 0 0 10px rgba(0, 0, 0, 0.6);
border-radius: 10px;
}
.cal-bg {
position: absolute;
width: 956px;
top: 0;
left: 0;
right: 0;
bottom: 0;
text-align: center;
border-collapse: collapse;
height: 100%;
box-shadow: 0 0 1px 0 #fff inset;
border-radius: 10px;
overflow: hidden;
}
.cal-bg table {
height: 100%;
}
.cal-bg .tr.thead {
background: rgba(0, 0, 0, 0.8);
height: 40px;
}
.cal-bg td {
box-shadow: 0 0 1px 0 #fff;
}
.cal-bg td.date {
width: 7.692%;
}
.cal-bg td.date span {
display: block;
line-height: 18px;
}
.cal-bg td.date span.date-week {
line-height: 12px;
font-size: 12px;
color: #888;
}
.cal-bg td.date.current-date {
background: #d3bc8e;
border: 1px solid #d3bc8e;
color: #000;
}
.cal-bg td.date.current-date span.date-week {
color: #000;
}
.cal-bg td.line {
background: rgba(0, 0, 0, 0.4);
vertical-align: top;
}
.cal-bg td.line.current-date {
background: rgba(211, 188, 142, 0.4);
}
.cal-bg .card {
width: 65px;
height: 76px;
margin: 8px auto -4px;
}
.cal-bg .card .img {
height: 60px;
}
.cal-bg .card .char-name {
position: absolute;
bottom: 0;
left: 0;
right: 0;
line-height: 17px;
font-size: 12px;
background: #e8e2d8;
}
.cal-list {
position: relative;
padding-top: 80px;
overflow: hidden;
}
.cal-list.char-num-0 {
padding-top: 80px;
}
.cal-list.char-num-1 {
padding-top: 160px;
}
.cal-list.char-num-2 {
padding-top: 240px;
}
.cal-list.char-num-3 {
padding-top: 320px;
}
.cal-list .cal-item {
margin-bottom: 15px;
border-radius: 5px;
white-space: nowrap;
text-overflow: ellipsis;
position: relative;
overflow: hidden;
background: #e8e2d8;
z-index: 1;
}
.cal-list .cal-item:after {
content: "";
display: block;
position: absolute;
left: 3px;
top: 3px;
right: 4px;
bottom: 4px;
box-shadow: 0 0 1px 0 #000 inset, 0 0 2px 0 #222a3b;
border-radius: 4px;
}
.cal-list .cal-item .info {
position: relative;
display: inline-block;
padding: 15px 50px 15px 55px;
min-width: calc(100% - 400px);
border-radius: 5px;
background-image: linear-gradient(to right, #E8E2D8, #E8E2D8 80%, rgba(232, 226, 216, 0) 100%);
}
.cal-list .cal-item .banner {
position: absolute;
width: 100%;
max-width: 500px;
top: 0;
bottom: 0;
right: 0;
background-size: 100% auto;
background-position: left 40%;
}
.cal-list .cal-item strong {
display: block;
font-weight: normal;
}
.cal-list .cal-item span {
display: block;
font-size: 12px;
}
.cal-list .cal-item.type-character {
overflow: visible;
margin-top: 20px;
}
.cal-list .cal-item.type-character .info {
padding-left: 65px;
}
.cal-list .cal-item.type-character .character-img {
height: 75px;
position: absolute;
bottom: 0;
left: 0;
z-index: 10;
}
.cal-list .cal-item.type-normal {
margin-top: 5px;
margin-bottom: 5px;
}
.cal-list .cal-item.type-normal:last-child {
margin-bottom: 15px;
}
.cal-list .cal-item.type-normal:after {
display: none;
}
.cal-list .cal-item.type-normal:first-of-type {
margin-top: 20px;
}
.cal-list .cal-item.type-normal .info {
padding: 8px 20px 8px 15px;
line-height: 16px;
color: #4b5366;
}
.cal-list .cal-item.type-normal .cal-icon {
width: 23px;
height: 23px;
top: 6px;
margin-left: -3px;
margin-right: 5px;
}
.cal-list .cal-item.type-normal strong {
font-size: 16px;
}
.cal-list .cal-item.type-normal .info {
padding-left: 38px;
}
.cal-list .cal-item.type-normal strong,
.cal-list .cal-item.type-normal span {
display: inline;
}
.cal-list .cal-item.type-normal.small-mode span {
display: block;
margin-left: 0;
}
.cal-list .cal-item.type-normal.li-col1 {
margin-top: -40px;
}
.cal-list .cal-item .cal-icon {
position: absolute;
width: 40px;
height: 40px;
left: 10px;
top: 10px;
}
.cal-list .cal-item.li-col1 {
margin-top: -82px;
}
.cal-list.char-2-1 .type-character.li-idx-2,
.cal-list.char-3-1 .type-character.li-idx-2 {
margin-top: -82px;
}
.cal-list.char-3-2 .type-character.li-idx-3 {
margin-top: -82px;
}
.cal-list.char-4-2 .type-character.li-idx-3 {
margin-top: -166px;
}
.cal-list .type-weapon.li-idx-2 {
margin-top: -82px;
}
.calendar .now-line {
position: absolute;
top: 86px;
bottom: -18px;
width: 2px;
box-shadow: 0 0 5px 0 #fff;
background: #fff;
opacity: 0.8;
}
.calendar .now-line:after {
content: "";
display: block;
width: 0;
height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 20px solid #fff;
position: absolute;
bottom: -8px;
left: -9px;
transform: scaleY(0.7);
transform-origin: bottom center;
}
.calendar .now-line.line2 {
z-index: 3;
opacity: 0.5;
background: #d3bc8d;
width: 2px;
box-shadow: none;
}
.now-time {
text-align: center;
padding-top: 5px;
margin-bottom: 5px;
}
.now-time span {
color: #fff;
background: rgba(0, 0, 0, 0.6);
border-radius: 30px;
padding: 10px 15px;
border: 1px solid #fff;
display: inline-block;
}
.cal-abyss-cont {
padding-top: 15px;
height: 80px;
position: relative;
}
.cal-abyss-cont .cal-item {
border-radius: 0;
background: url("img/abyss.jpg") #333465 top right no-repeat;
position: absolute;
}
.cal-abyss-cont .cal-item .info {
background: none;
color: #d3bc8d;
background-image: linear-gradient(to right, #333465, #333465 80%, rgba(51, 52, 101, 0) 100%);
}
.cal-abyss-cont .cal-item:before {
content: "";
display: block;
width: 3px;
left: 0;
top: 1px;
bottom: 1px;
position: absolute;
background: #d3bc8d;
z-index: 8;
}
.cal-abyss-cont .cal-item:after {
box-shadow: 0 0 1px 0 #fff;
border-radius: 0;
}
.calendar-mode .for-list-mode {
display: none;
}
.list-mode .container {
width: 740px;
}
.list-mode .for-calendar-mode {
display: none;
}
.list-mode .cal-bg {
width: initial;
}
.list-mode .cal-list {
padding: 45px 10px 0;
}
.list-mode .calendar {
width: 700px;
}
.list-mode .cal-abyss-cont {
height: initial !important;
}
.list-mode .cal-item {
position: relative;
margin-left: 0 !important;
width: initial !important;
left: 0 !important;
}
.list-mode .now-line {
display: none;
}
.daily-talent {
display: flex;
flex-wrap: wrap;
margin: 5px 10px 0;
background: rgba(0, 0, 0, 0.5);
padding: 10px 9px 10px;
border-radius: 10px;
}
.daily-talent .item-icon {
overflow: visible;
}
.daily-talent .card {
width: 87px;
height: 105px;
margin: 10px 0 15px;
}
.daily-talent .card .item-icon {
width: 77px;
margin: 0 6px;
height: 82px;
padding-top: 5px;
}
.daily-talent .card .img {
width: 77px;
height: 77px;
}
.daily-talent .card .weekly {
position: absolute;
width: 24px;
height: 24px;
border-radius: 50%;
bottom: -10px;
right: -3px;
background-color: rgba(232, 226, 216, 0.9);
box-shadow: 0 0 2px 0 #000;
overflow: visible;
}
.daily-talent .card .weekly .weekly-icon {
width: 30px;
height: 30px;
margin: -3px;
background-size: contain;
background-position: center;
background-repeat: no-repeat;
}
.daily-talent .card .banner {
height: 20px;
padding-top: 1px;
line-height: 20px;
color: #fff;
position: relative;
margin-bottom: 8px;
}
.daily-talent .card .banner .title {
margin-right: -50px;
width: calc(100% + 50px);
display: flex;
position: absolute;
top: 0;
left: 0;
z-index: 2;
text-shadow: 0 0 1px rgba(0, 0, 0, 0.8), 1px 1px 2px rgba(0, 0, 0, 0.8);
padding-left: 45px;
font-size: 18px;
}
.daily-talent .card .banner .icon {
width: 40px;
height: 40px;
background-size: contain;
background-position: center;
background-repeat: no-repeat;
display: inline-block;
position: absolute;
left: 0;
top: -8px;
}
.daily-talent .card .banner .line {
height: 6px;
width: 100%;
margin-top: 13px;
}
.daily-talent .card .banner .line.first {
margin-left: 35%;
width: 65%;
border-radius: 3px 0 0 3px;
}
.daily-talent .card .banner .line.last {
width: 94%;
border-radius: 0 3px 3px 0;
}
.daily-talent .card .banner.city-1 .line {
background: #37c9b8;
}
.daily-talent .card .banner.city-2 .line {
background: #bca244;
}
.daily-talent .card .banner.city-3 .line {
background: #ac60c9;
}
.daily-talent .card .banner.city-4 .line {
background: #54b640;
}
/*# sourceMappingURL=calendar.css.map */

View File

@ -0,0 +1,103 @@
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<link rel="shortcut icon" href="#"/>
<link rel="stylesheet" type="text/css" href="common.css"/>
<title>Calendar</title>
<link rel="stylesheet" type="text/css" href="calendar.css"/>
</head>
<body class="elem-hydro {{ display_mode }}-mode" style=transform:scale(1)>
<div class="container elem-bg" id="container">
<div class="calendar">
<div class="cal-bg for-calendar-mode">
<table class="cont-table" border-collapse="collapse">
<tr class="tr thead">
{% for d in date_list %}
<td colspan="{{ d.date.__len__() }}" class="td month">{{ d.month }}月</td>
{% endfor %}
</tr>
<tr class="tr thead">
{% for d in date_list %}
{% for dn in range(d.date.__len__()) %}
<td class="td date {{ 'current-date' if d.is_today[dn] else ''}}">
<span class="date-num">{{ d.date[dn] }}日</span>
<span class="date-week">周{{ d.week[dn] }}</span>
</td>
{% endfor %}
{% endfor %}
</tr>
<tr class="tr">
{% for d in date_list %}
{% for dn in range(d.date.__len__()) %}
<td class="line {{ 'current-date' if d.is_today[dn] else ''}}">
{% for char in birthday_chars[d.month|string][d.date[dn]|string] %}
<div class="card">
<div class="item-icon star{{ char.star }}">
<div class="img" style="background-image:url('{{ char.icon }}')"></div>
<span class="char-name">{{ char.name }}{{ '生日' if char.name.__len__() < 4 else '' }}</span>
</div>
</div>
{% endfor %}
</td>
{% endfor %}
{% endfor %}
</tr>
</table>
</div>
<div class="cal-bg for-list-mode">
<table class="cont-table" border-collapse="collapse">
<tr class="tr thead">
<td class="td month">活动列表</td>
</tr>
<tr class="tr">
<td class="line"></td>
</tr>
</table>
</div>
<div class="cal-list {{ char_mode }} char-num-{{ birthday_char_line }}">
<div class="cal-abyss-cont">
{% for li in abyss %}
<div class="cal-item type-abyss" style="left:{{ li.left }}%;width:{{li.width}}%">
<div class="info">
<img src="img/abyss-icon.png" class="cal-icon"/>
<strong>{{ li.title }}</strong>
<span>{{ li.label }}</span>
</div>
</div>
{% endfor %}
</div>
{% for lis in list %}
{% for li in lis %}
<div
class="cal-item type-{{ li.type }} {{ 'li-idx-' if li.idx else ''}}{{ li.idx if li.idx else '' }} {{'small-mode' if li.width < 20 else ''}} li-col{{ lis.index(li) }}"
style="left:{{ li.left }}%;width:{{li.width}}%"
data-id="{{li.id}}"
data-type="{{li.type}}">
{% if li.banner %}
<div class="banner" style="background-image:url('{{li.banner}}')"></div>
{% endif %}
<div class="info">
{% if li.type == "character" %}
<img src="{{ li.face }}" class="character-img"/>
{% else %}
<img src="{{ li.icon }}" class="cal-icon"/>
{% endif %}
<strong>{{ li.title }}</strong>
<span>{{ li.label }}</span>
</div>
</div>
{% endfor %}
{% endfor %}
</div>
<div class="now-line" style="left:{{now_left}}%"></div>
<div class="now-line line2" style="left:{{now_left}}%"></div>
</div>
<div class="now-time">
<span>{{ now_time }}</span>
</div>
<div class="copyright">Inspired By Miao-Plugin</div>
</div>
</body>
</html>

View File

@ -0,0 +1,573 @@
//linear-gradient(to right, rgba(232, 226, 216, 1), rgba(232, 226, 216, 1) 80%, rgba(232, 226, 216, 0) 100%);
.linear-bg(@color) {
background-image: linear-gradient(to right, @color, @color 80%, fade(@color, 0) 100%);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
}
body {
font-size: 18px;
color: #1e1f20;
transform: scale(1);
transform-origin: 0 0;
width: 996px;
}
.container {
width: 996px;
padding: 10px 0px 10px 0px;
background-size: 100% 100%;
}
.logo {
font-size: 18px;
text-align: center;
color: #fff;
margin: 20px 0 10px 0;
}
.calendar {
min-height: 400px;
position: relative;
padding: 1px 0;
width: 956px;
margin: 20px;
box-shadow: 0 0 0 10px rgba(0, 0, 0, .6);
border-radius: 10px;
}
.cal-bg {
position: absolute;
width: 956px;
top: 0;
left: 0;
right: 0;
bottom: 0;
text-align: center;
border-collapse: collapse;
height: 100%;
box-shadow: 0 0 1px 0 #fff inset;
border-radius: 10px;
overflow: hidden;
table {
height: 100%;
}
.tr.thead {
background: rgba(0, 0, 0, .8);
height: 40px;
td {
}
}
td {
box-shadow: 0 0 1px 0 #fff;
&.date {
width: 7.692%;
span {
display: block;
line-height: 18px;
}
span.date-num {
}
span.date-week {
line-height: 12px;
font-size: 12px;
color: #888;
}
&.current-date {
background: #d3bc8e;
border: 1px solid #d3bc8e;
color: #000;
span.date-week {
color: #000;
}
}
}
&.line {
background: rgba(0, 0, 0, 0.4);
vertical-align: top;
&.current-date {
background: rgba(211, 188, 142, .4)
}
}
}
.card {
width: 65px;
height: 76px;
margin: 8px auto -4px;
.img {
height: 60px;
}
.char-name {
position: absolute;
bottom: 0;
left: 0;
right: 0;
line-height: 17px;
font-size: 12px;
background: #e8e2d8;
}
}
}
.cal-list {
position: relative;
padding-top: 80px;
overflow: hidden;
&.char-num-0 {
padding-top: 80px;
}
&.char-num-1 {
padding-top: 160px;
}
&.char-num-2 {
padding-top: 240px;
}
&.char-num-3 {
padding-top: 320px;
}
.cal-item {
margin-bottom: 15px;
border-radius: 5px;
white-space: nowrap;
text-overflow: ellipsis;
position: relative;
overflow: hidden;
background: rgba(232, 226, 216, 1);
z-index: 1;
&:after {
content: "";
display: block;
position: absolute;
left: 3px;
top: 3px;
right: 4px;
bottom: 4px;
box-shadow: 0 0 1px 0 #000 inset, 0 0 2px 0 #222a3b;
border-radius: 4px;
}
.info {
position: relative;
display: inline-block;
padding: 15px 50px 15px 55px;
min-width: calc(100% - 400px);
border-radius: 5px;
.linear-bg(#E8E2D8);
}
.banner {
position: absolute;
width: 100%;
max-width: 500px;
top: 0;
bottom: 0;
right: 0;
background-size: 100% auto;
background-position: left 40%;
}
strong {
display: block;
font-weight: normal;
}
span {
display: block;
font-size: 12px;
}
&.type-character {
overflow: visible;
margin-top: 20px;
.info {
padding-left: 65px;
}
.character-img {
height: 75px;
position: absolute;
bottom: 0;
left: 0;
z-index: 10;
}
}
&.type-normal {
margin-top: 5px;
margin-bottom: 5px;
&:last-child {
margin-bottom: 15px;
}
&:after {
display: none;
}
&:first-of-type {
margin-top: 20px;
}
.info {
padding: 8px 20px 8px 15px;
line-height: 16px;
color: #4b5366;
}
.cal-icon {
width: 23px;
height: 23px;
top: 6px;
margin-left: -3px;
margin-right: 5px;
}
strong {
font-size: 16px;
}
.info {
padding-left: 38px;
}
strong,
span {
display: inline;
}
&.small-mode span {
display: block;
margin-left: 0;
}
&.li-col1 {
margin-top: -40px;
}
}
.cal-icon {
position: absolute;
width: 40px;
height: 40px;
left: 10px;
top: 10px;
}
&.li-col1 {
margin-top: -82px;
}
}
&.char-2-1,
&.char-3-1 {
.type-character.li-idx-2 {
margin-top: -82px;
}
}
&.char-3-2 {
.type-character.li-idx-3 {
margin-top: -82px;
}
}
&.char-4-2 {
.type-character.li-idx-3 {
margin-top: -166px;
}
}
.type-weapon.li-idx-2 {
margin-top: -82px;
}
}
.calendar .now-line {
position: absolute;
top: 86px;
bottom: -18px;
width: 2px;
box-shadow: 0 0 5px 0 #fff;
background: #fff;
opacity: .8;
&:after {
content: "";
display: block;
width: 0;
height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 20px solid #fff;
position: absolute;
bottom: -8px;
left: -9px;
transform: scaleY(.7);
transform-origin: bottom center;
}
&.line2 {
z-index: 3;
opacity: .5;
background: rgb(211, 188, 141);
width: 2px;
box-shadow: none;
&:after {
}
}
}
.now-time {
text-align: center;
padding-top: 5px;
margin-bottom: 5px;
span {
color: #fff;
background: rgba(0, 0, 0, 0.6);
border-radius: 30px;
padding: 10px 15px;
border: 1px solid #fff;
display: inline-block;
}
}
.cal-abyss-cont {
padding-top: 15px;
height: 80px;
position: relative;
.cal-item {
border-radius: 0;
background: url("img/abyss.jpg") #333465 top right no-repeat;
position: absolute;
.info {
background: none;
color: rgba(211, 188, 141, 1);
.linear-bg(#333465);
}
&:before {
content: "";
display: block;
width: 3px;
left: 0;
top: 1px;
bottom: 1px;
position: absolute;
background: #d3bc8d;
z-index: 8;
}
&:after {
box-shadow: 0 0 1px 0 #fff;
border-radius: 0;
}
}
}
.calendar-mode {
.for-list-mode {
display: none;
}
}
.list-mode {
.container {
width: 740px;
}
.for-calendar-mode {
display: none;
}
.cal-bg {
width: initial;
}
.cal-list {
padding: 45px 10px 0;
}
.calendar {
width: 700px;
}
.cal-abyss-cont {
height: initial !important;
}
.cal-item {
position: relative;
margin-left: 0 !important;
width: initial !important;
left: 0 !important;
}
.now-line {
display: none;
}
}
@width: 77px;
.daily-talent {
display: flex;
flex-wrap: wrap;
margin: 5px 10px 0;
background: rgba(0, 0, 0, .5);
padding: 10px 9px 10px;
border-radius: 10px;
.item-icon {
overflow: visible;
}
.card {
width: @width + 10px;
height: @width + 28px;
margin: 10px 0 15px;
.item-icon {
width: @width;
margin: 0 6px;
height: @width + 5px;
padding-top: 5px;
}
.img {
width: @width;
height: @width;
}
.weekly {
position: absolute;
width: 24px;
height: 24px;
border-radius: 50%;
bottom: -10px;
right: -3px;
background-color: rgba(232, 226, 216, 0.9);
box-shadow: 0 0 2px 0 #000;
overflow: visible;
.weekly-icon {
width: 30px;
height: 30px;
margin: -3px;
background-size: contain;
background-position: center;
background-repeat: no-repeat;
}
}
.banner {
height: 20px;
padding-top: 1px;
line-height: 20px;
color: #fff;
position: relative;
margin-bottom: 8px;
.title {
margin-right: -50px;
width: calc(100% + 50px);
display: flex;
position: absolute;
top: 0;
left: 0;
z-index: 2;
text-shadow: 0 0 1px rgba(0, 0, 0, .8), 1px 1px 2px rgba(0, 0, 0, .8);
padding-left: 45px;
font-size: 18px;
}
.icon {
width: 40px;
height: 40px;
background-size: contain;
background-position: center;
background-repeat: no-repeat;
display: inline-block;
position: absolute;
left: 0;
top: -8px;
}
.line {
height: 6px;
width: 100%;
margin-top: 13px;
&.first {
margin-left: 35%;
width: 65%;
border-radius: 3px 0 0 3px;
}
&.last {
width: 94%;
border-radius: 0 3px 3px 0;
}
}
.city(@name, @bg) {
&.city-@{name} .line {
background: @bg;
}
}
.city(1, #37c9b8);
.city(2, #bca244);
.city(3, #ac60c9);
.city(4, #54b640);
}
}
}

View File

@ -0,0 +1,521 @@
@font-face {
font-family: 'Number';
src: url("../../fonts/tttgbnumber.ttf") format('truetype');
}
@font-face {
font-family: 'YS';
src: url("../../fonts/HYWenHei-65W.ttf") format('truetype');
}
.font-ys {
font-family: Number, "汉仪文黑-65W", YS, PingFangSC-Medium, "PingFang SC", sans-serif;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
-webkit-user-select: none;
user-select: none;
}
body {
font-size: 18px;
color: #1e1f20;
font-family: Number, "汉仪文黑-65W", YS, PingFangSC-Medium, "PingFang SC", sans-serif;
transform: scale(1.4);
transform-origin: 0 0;
width: 600px;
}
.container {
width: 600px;
padding: 20px 15px 10px 15px;
background-size: contain;
}
.head-box {
border-radius: 15px;
padding: 10px 20px;
position: relative;
color: #fff;
margin-top: 30px;
}
.head-box .title {
font-family: Number, "汉仪文黑-65W", YS, PingFangSC-Medium, "PingFang SC", sans-serif;
font-size: 36px;
text-shadow: 0 0 1px #000, 1px 1px 3px rgba(0, 0, 0, 0.9);
}
.head-box .title .label {
display: inline-block;
margin-left: 10px;
}
.head-box .genshin_logo {
position: absolute;
top: 1px;
right: 15px;
width: 97px;
}
.head-box .label {
font-size: 16px;
text-shadow: 0 0 1px #000, 1px 1px 3px rgba(0, 0, 0, 0.9);
}
.head-box .label span {
color: #d3bc8e;
padding: 0 2px;
}
.notice {
color: #888;
font-size: 12px;
text-align: right;
padding: 12px 5px 5px;
}
.notice-center {
color: #fff;
text-align: center;
margin-bottom: 10px;
text-shadow: 1px 1px 1px #333;
}
.copyright {
font-size: 14px;
text-align: center;
color: #fff;
position: relative;
padding-left: 10px;
text-shadow: 1px 1px 1px #000;
margin: 10px 0;
}
.copyright .version {
color: #d3bc8e;
display: inline-block;
padding: 0 3px;
}
/* */
.cons {
display: inline-block;
vertical-align: middle;
padding: 0 5px;
border-radius: 4px;
}
.cons-0 {
background: #666;
color: #fff;
}
.cons-n0 {
background: #404949;
color: #fff;
}
.cons-1 {
background: #5cbac2;
color: #fff;
}
.cons-2 {
background: #339d61;
color: #fff;
}
.cons-3 {
background: #3e95b9;
color: #fff;
}
.cons-4 {
background: #3955b7;
color: #fff;
}
.cons-5 {
background: #531ba9cf;
color: #fff;
}
.cons-6 {
background: #ff5722;
color: #fff;
}
.cons2-0 {
border-radius: 4px;
background: #666;
color: #fff;
}
.cons2-1 {
border-radius: 4px;
background: #71b1b7;
color: #fff;
}
.cons2-2 {
border-radius: 4px;
background: #369961;
color: #fff;
}
.cons2-3 {
border-radius: 4px;
background: #4596b9;
color: #fff;
}
.cons2-4 {
border-radius: 4px;
background: #4560b9;
color: #fff;
}
.cons2-5 {
border-radius: 4px;
background: #531ba9cf;
color: #fff;
}
.cons2-6 {
border-radius: 4px;
background: #ff5722;
color: #fff;
}
/******** Fetter ********/
.fetter {
width: 50px;
height: 50px;
display: inline-block;
background: url('img/fetter.png');
background-size: auto 100%;
}
.fetter.fetter1 {
background-position: 0% 0;
}
.fetter.fetter2 {
background-position: 11.11111111% 0;
}
.fetter.fetter3 {
background-position: 22.22222222% 0;
}
.fetter.fetter4 {
background-position: 33.33333333% 0;
}
.fetter.fetter5 {
background-position: 44.44444444% 0;
}
.fetter.fetter6 {
background-position: 55.55555556% 0;
}
.fetter.fetter7 {
background-position: 66.66666667% 0;
}
.fetter.fetter8 {
background-position: 77.77777778% 0;
}
.fetter.fetter9 {
background-position: 88.88888889% 0;
}
.fetter.fetter10 {
background-position: 100% 0;
}
/******** ELEM ********/
.elem-hydro .talent-icon {
background-image: url("../player_card/img/talent-hydro.png");
}
.elem-hydro .elem-bg,
.hydro-bg {
background-image: url("../player_card/img/bg-hydro.jpg");
}
.elem-anemo .talent-icon {
background-image: url("../player_card/img/talent-anemo.png");
}
.elem-anemo .elem-bg,
.anemo-bg {
background-image: url("../player_card/img/bg-anemo.jpg");
}
.elem-cryo .talent-icon {
background-image: url("../player_card/img/talent-cryo.png");
}
.elem-cryo .elem-bg,
.cryo-bg {
background-image: url("../player_card/img/bg-cryo.jpg");
}
.elem-electro .talent-icon {
background-image: url("../player_card/img/talent-electro.png");
}
.elem-electro .elem-bg,
.electro-bg {
background-image: url("../player_card/img/bg-electro.jpg");
}
.elem-geo .talent-icon {
background-image: url("../player_card/img/talent-geo.png");
}
.elem-geo .elem-bg,
.geo-bg {
background-image: url("../player_card/img/bg-geo.jpg");
}
.elem-pyro .talent-icon {
background-image: url("../player_card/img/talent-pyro.png");
}
.elem-pyro .elem-bg,
.pyro-bg {
background-image: url("../player_card/img/bg-pyro.jpg");
}
.elem-dendro .talent-icon {
background-image: url("../player_card/img/talent-dendro.png");
}
.elem-dendro .elem-bg,
.dendro-bg {
background-image: url("../player_card/img/bg-dendro.jpg");
}
/* cont */
.cont {
border-radius: 10px;
background: url("img/card-bg.png") top left repeat-x;
background-size: auto 100%;
margin: 5px 15px 5px 10px;
position: relative;
box-shadow: 0 0 1px 0 #ccc, 2px 2px 4px 0 rgba(50, 50, 50, 0.8);
overflow: hidden;
color: #fff;
font-size: 16px;
}
.cont-title {
background: rgba(0, 0, 0, 0.4);
box-shadow: 0 0 1px 0 #fff;
color: #d3bc8e;
padding: 10px 20px;
text-align: left;
border-radius: 10px 10px 0 0;
}
.cont-title span {
font-size: 12px;
color: #aaa;
margin-left: 10px;
font-weight: normal;
}
.cont-title.border-less {
background: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
box-shadow: none;
padding-bottom: 5px;
}
.cont-body {
padding: 10px 15px;
font-size: 12px;
background: rgba(0, 0, 0, 0.5);
box-shadow: 0 0 1px 0 #fff;
font-weight: normal;
}
.cont-footer {
padding: 10px 15px;
font-size: 12px;
background: rgba(0, 0, 0, 0.5);
font-weight: normal;
}
.cont > ul.cont-msg {
display: block;
padding: 5px 10px;
background: rgba(0, 0, 0, 0.5);
}
ul.cont-msg,
.cont-footer ul {
padding-left: 15px;
}
ul.cont-msg li,
.cont-footer ul li {
margin: 5px 0;
margin-left: 15px;
}
ul.cont-msg li strong,
.cont-footer ul li strong {
font-weight: normal;
margin: 0 2px;
color: #d3bc8e;
}
.cont-table {
display: table;
width: 100%;
}
.cont-table .tr {
display: table-row;
}
.cont-table .tr:nth-child(even) {
background: rgba(0, 0, 0, 0.4);
}
.cont-table .tr:nth-child(odd) {
background: rgba(50, 50, 50, 0.4);
}
.cont-table .tr > div,
.cont-table .tr > td {
display: table-cell;
box-shadow: 0 0 1px 0 #fff;
}
.cont-table .tr > div.value-full {
display: table;
width: 200%;
}
.cont-table .tr > div.value-none {
box-shadow: none;
}
.cont-table .thead {
text-align: center;
}
.cont-table .thead > div,
.cont-table .thead > td {
color: #d3bc8e;
background: rgba(0, 0, 0, 0.4);
line-height: 40px;
height: 40px;
}
.cont-table .title,
.cont-table .th {
color: #d3bc8e;
padding-right: 15px;
text-align: right;
background: rgba(0, 0, 0, 0.4);
min-width: 100px;
vertical-align: middle;
}
.logo {
font-size: 18px;
text-align: center;
color: #fff;
margin: 20px 0 10px 0;
}
/* item-icon */
.item-icon {
width: 100%;
height: 100%;
border-radius: 4px;
position: relative;
overflow: hidden;
}
.item-icon .img {
width: 100%;
height: 100%;
display: block;
background-size: contain;
background-position: center;
background-repeat: no-repeat;
}
.item-icon.artis .img {
width: 84%;
height: 84%;
margin: 8%;
}
.item-icon.star4 {
background-image: url("../abyss/background/roleStarBg4.png");
}
.item-icon.opacity-bg.star4 {
background-image: url("../abyss/background/roleStarBg4.png");
}
.item-icon.star5 {
background-image: url("../abyss/background/roleStarBg5.png");
}
.item-icon.opacity-bg.star5 {
background-image: url("../abyss/background/roleStarBg5.png");
}
.item-list {
display: flex;
}
.item-list .item-card {
width: 70px;
background: #e7e5d9;
}
.item-list .item-icon {
border-bottom-left-radius: 0;
border-bottom-right-radius: 12px;
}
.item-list .item-title {
color: #222;
font-size: 13px;
text-align: center;
padding: 2px;
white-space: nowrap;
overflow: hidden;
}
.item-list .item-icon {
height: initial;
}
.item-list .item-badge {
position: absolute;
display: block;
left: 0;
top: 0;
background: rgba(0, 0, 0, 0.6);
font-size: 12px;
color: #fff;
padding: 4px 5px 3px;
border-radius: 0 0 6px 0;
}
/*# sourceMappingURL=common.css.map */

View File

@ -0,0 +1,391 @@
.font(@name, @file) {
@font-face {
font-family: @name;
src: url("../../fonts/@{file}.ttf") format('truetype');
}
}
.font('Number', 'tttgbnumber');
.font('YS', 'HYWenHei-65W');
.font-ys {
font-family: Number, "汉仪文黑-65W", YS, PingFangSC-Medium, "PingFang SC", sans-serif;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
-webkit-user-select: none;
user-select: none;
}
body {
font-size: 18px;
color: #1e1f20;
font-family: Number, "汉仪文黑-65W", YS, PingFangSC-Medium, "PingFang SC", sans-serif;
transform: scale(1.4);
transform-origin: 0 0;
width: 600px;
}
.container {
width: 600px;
padding: 20px 15px 10px 15px;
background-size: contain;
}
.head-box {
border-radius: 15px;
padding: 10px 20px;
position: relative;
color: #fff;
margin-top: 30px;
.title {
.font-ys;
font-size: 36px;
text-shadow: 0 0 1px #000, 1px 1px 3px rgba(0, 0, 0, .9);
.label {
display: inline-block;
margin-left: 10px;
}
}
.genshin_logo {
position: absolute;
top: 1px;
right: 15px;
width: 97px;
}
.label {
font-size: 16px;
text-shadow: 0 0 1px #000, 1px 1px 3px rgba(0, 0, 0, .9);
span {
color: #d3bc8e;
padding: 0 2px;
}
}
}
.notice {
color: #888;
font-size: 12px;
text-align: right;
padding: 12px 5px 5px;
}
.notice-center {
color: #fff;
text-align: center;
margin-bottom: 10px;
text-shadow: 1px 1px 1px #333;
}
.copyright {
font-size: 14px;
text-align: center;
color: #fff;
position: relative;
padding-left: 10px;
text-shadow: 1px 1px 1px #000;
margin: 10px 0;
.version {
color: #d3bc8e;
display: inline-block;
padding: 0 3px;
}
}
/* */
.cons {
display: inline-block;
vertical-align: middle;
padding: 0 5px;
border-radius: 4px;
}
.cons(@idx, @bg, @color:#fff) {
.cons-@{idx} {
background: @bg;
color: @color;
}
}
.cons(0, #666);
.cons(n0, #404949);
.cons(1, #5cbac2);
.cons(2, #339d61);
.cons(3, #3e95b9);
.cons(4, #3955b7);
.cons(5, #531ba9cf);
.cons(6, #ff5722);
.cons2(@idx, @bg, @color:#fff) {
.cons2-@{idx} {
border-radius: 4px;
background: @bg;
color: @color;
}
}
.cons2(0, #666);
.cons2(1, #71b1b7);
.cons2(2, #369961);
.cons2(3, #4596b9);
.cons2(4, #4560b9);
.cons2(5, #531ba9cf);
.cons2(6, #ff5722);
/******** Fetter ********/
.fetter {
width: 50px;
height: 50px;
display: inline-block;
background: url('img/fetter.png');
background-size: auto 100%;
@fetters: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10;
each(@fetters, {
&.fetter@{value} {
background-position: (-100%/9)+(100%/9)*@value 0;
}
})
}
/******** ELEM ********/
@elems: hydro, anemo, cryo, electro, geo, pyro, dendro;
each(@elems, {
.elem-@{value} .talent-icon {
background-image: url("../player_card/img/talent-@{value}.png");
}
.elem-@{value} .elem-bg,
.@{value}-bg {
background-image: url("../player_card/img/bg-@{value}.jpg");
}
})
/* cont */
.cont {
border-radius: 10px;
background: url("img/card-bg.png") top left repeat-x;
background-size: auto 100%;
// backdrop-filter: blur(3px);
margin: 5px 15px 5px 10px;
position: relative;
box-shadow: 0 0 1px 0 #ccc, 2px 2px 4px 0 rgba(50, 50, 50, .8);
overflow: hidden;
color: #fff;
font-size: 16px;
}
.cont-title {
background: rgba(0, 0, 0, .4);
box-shadow: 0 0 1px 0 #fff;
color: #d3bc8e;
padding: 10px 20px;
text-align: left;
border-radius: 10px 10px 0 0;
span {
font-size: 12px;
color: #aaa;
margin-left: 10px;
font-weight: normal;
}
&.border-less {
background: linear-gradient(rgba(0, 0, 0, .5), rgba(0, 0, 0, 0));
box-shadow: none;
padding-bottom: 5px;
}
}
.cont-body {
padding: 10px 15px;
font-size: 12px;
background: rgba(0, 0, 0, 0.5);
box-shadow: 0 0 1px 0 #fff;
font-weight: normal;
}
.cont-footer {
padding: 10px 15px;
font-size: 12px;
background: rgba(0, 0, 0, 0.5);
font-weight: normal;
}
.cont > ul.cont-msg {
display: block;
padding: 5px 10px;
background: rgba(0, 0, 0, 0.5);
}
ul.cont-msg, .cont-footer ul {
padding-left: 15px;
li {
margin: 5px 0;
margin-left: 15px;
strong {
font-weight: normal;
margin: 0 2px;
color: #d3bc8e;
}
}
}
.cont-table {
display: table;
width: 100%;
}
.cont-table .tr {
display: table-row;
}
.cont-table .tr:nth-child(even) {
background: rgba(0, 0, 0, .4);
}
.cont-table .tr:nth-child(odd) {
background: rgba(50, 50, 50, .4);
}
.cont-table .tr > div,
.cont-table .tr > td {
display: table-cell;
box-shadow: 0 0 1px 0 #fff;
}
.cont-table .tr > div.value-full {
display: table;
width: 200%;
}
.cont-table .tr > div.value-none {
box-shadow: none;
}
.cont-table .thead {
text-align: center;
}
.cont-table .thead > div,
.cont-table .thead > td {
color: #d3bc8e;
background: rgba(0, 0, 0, .4);
line-height: 40px;
height: 40px;
}
.cont-table .title,
.cont-table .th {
color: #d3bc8e;
padding-right: 15px;
text-align: right;
background: rgba(0, 0, 0, .4);
min-width: 100px;
vertical-align: middle;
}
.logo {
font-size: 18px;
text-align: center;
color: #fff;
margin: 20px 0 10px 0;
}
/* item-icon */
.item-icon {
width: 100%;
height: 100%;
border-radius: 4px;
position: relative;
overflow: hidden;
.img {
width: 100%;
height: 100%;
display: block;
background-size: contain;
background-position: center;
background-repeat: no-repeat;
}
&.artis {
.img {
width: 84%;
height: 84%;
margin: 8%;
}
}
@stars: 4, 5;
each(@stars, {
&.star@{value} {
background-image: url("../abyss/background/roleStarBg@{value}.png");
}
&.opacity-bg.star@{value} {
background-image: url("../abyss/background/roleStarBg@{value}.png");
}
})
}
.item-list {
display: flex;
.item-card {
width: 70px;
background: #e7e5d9;
}
.item-icon {
border-bottom-left-radius: 0;
border-bottom-right-radius: 12px;
}
.item-title {
color: #222;
font-size: 13px;
text-align: center;
padding: 2px;
white-space: nowrap;
overflow: hidden;
}
.item-icon {
height: initial;
}
.item-badge {
position: absolute;
display: block;
left: 0;
top: 0;
background: rgba(0, 0, 0, 0.6);
font-size: 12px;
color: #fff;
padding: 4px 5px 3px;
border-radius: 0 0 6px 0;
}
}

View File

@ -0,0 +1,199 @@
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<link rel="shortcut icon" href="#"/>
<link rel="stylesheet" type="text/css" href="common.css"/>
<title>miao-plugin</title>
<link rel="stylesheet" type="text/css" href="calendar.css"/>
</head>
<body class="elem-hydro calendar-mode" style=transform:scale(1)>
<div class="container elem-bg" id="container">
<div class="calendar">
<div class="cal-bg for-calendar-mode">
<table class="cont-table" border-collapse="collapse">
<tr class="tr thead">
<td colspan="13" class="td month">2月</td>
</tr>
<tr class="tr thead">
<td class="td date">
<span class="date-num">9日</span>
<span class="date-week">周四</span>
</td>
<td class="td date">
<span class="date-num">10日</span>
<span class="date-week">周五</span>
</td>
<td class="td date">
<span class="date-num">11日</span>
<span class="date-week">周五</span>
</td>
<td class="td date">
<span class="date-num">12日</span>
<span class="date-week">周五</span>
</td>
<td class="td date">
<span class="date-num">13日</span>
<span class="date-week">周五</span>
</td>
<td class="td date">
<span class="date-num">14日</span>
<span class="date-week">周五</span>
</td>
<td class="td date current-date">
<span class="date-num">15日</span>
<span class="date-week">周五</span>
</td>
<td class="td date">
<span class="date-num">16日</span>
<span class="date-week">周五</span>
</td>
<td class="td date">
<span class="date-num">17日</span>
<span class="date-week">周五</span>
</td>
<td class="td date">
<span class="date-num">18日</span>
<span class="date-week">周五</span>
</td>
<td class="td date">
<span class="date-num">19日</span>
<span class="date-week">周五</span>
</td>
<td class="td date">
<span class="date-num">20日</span>
<span class="date-week">周五</span>
</td>
<td class="td date">
<span class="date-num">21日</span>
<span class="date-week">周五</span>
</td>
</tr>
<tr class="tr">
<td class="line">
</td>
<td class="line">
</td>
<td class="line">
</td>
<td class="line">
</td>
<td class="line">
</td>
<td class="line">
</td>
<td class="line current-date">
</td>
<td class="line">
</td>
<td class="line">
</td>
<td class="line">
</td>
<td class="line">
</td>
<td class="line">
</td>
<td class="line">
</td>
</tr>
</table>
</div>
<div class="cal-bg for-list-mode">
<table class="cont-table" border-collapse="collapse">
<tr class="tr thead">
<td class="td month">活动列表</td>
</tr>
<tr class="tr">
<td class="line"></td>
</tr>
</table>
</div>
<div class="cal-list char-2-2 char-num-0">
<div class="cal-abyss-cont">
<div class="cal-item type-abyss" style="left:0.0%;width:55.12811609691518%">
<div class="info">
<img src="img/abyss-icon.png" class="cal-icon"/>
<strong>「深境螺旋」· 2月上半</strong>
<span>02-01 04:00 ~ 02-16 03:59 (5小时2分钟后结束)</span>
</div>
</div>
<div class="cal-item type-abyss" style="left:55.1282051282542%;width:44.8717948717458%">
<div class="info">
<img src="img/abyss-icon.png" class="cal-icon"/>
<strong>「深境螺旋」· 2月下半</strong>
<span>02-16 04:00 (5小时2分钟后开始)</span>
</div>
</div>
</div>
<div
class="cal-item type-weapon li-col"
style="margin-left:0.0%;width:100.0%"
data-id="3429"
data-type="weapon">
<div class="banner" style="background-image:url('https://webstatic.mihoyo.com/upload/ann/2022/12/28/d9e5b0f28d65bc817d44cd1170f6b9a2_5522946055662469236.jpg')"></div>
<div class="info">
<img src='https://uploadstatic.mihoyo.com/announcement/2020/03/05/2cf7327b5fb2c050872ead29e338e368_7400353291716098710.png' class="cal-icon"/>
<strong>「神铸赋形」祈愿「护摩之杖」「若水」概率UP</strong>
<span>02-07 18:00 ~ 02-28 14:59 (12天15小时56分钟后结束)</span>
</div>
</div>
<div
class="cal-item type-activity li-col"
style="margin-left:0.0%;width:100.0%"
data-id="3420"
data-type="activity">
<div class="banner" style="background-image:url('https://webstatic.mihoyo.com/upload/ann/2022/12/28/dfdb4fe78124efbd888bb11d90481e3b_1185432047750172096.jpg')"></div>
<div class="info">
<img src='https://uploadstatic.mihoyo.com/announcement/2020/03/05/f3016cc0dbe3f9c2305566742ae5927f_1830032474842461374.png' class="cal-icon"/>
<strong>「花时来信」神里绫华衣装限时折扣</strong>
<span>01-18 11:00 ~ 02-27 03:59 (11天4小时56分钟后结束)</span>
</div>
</div>
<div
class="cal-item type-pass li-col"
style="margin-left:0.0%;width:100.0%"
data-id="3421"
data-type="pass">
<div class="banner" style="background-image:url('https://webstatic.mihoyo.com/upload/ann/2022/12/28/760d459815947d53014f57497228d35b_8021829647860262889.jpg')"></div>
<div class="info">
<img src='https://uploadstatic.mihoyo.com/announcement/2020/03/05/f3016cc0dbe3f9c2305566742ae5927f_1830032474842461374.png' class="cal-icon"/>
<strong>「合韵纪行」活动说明</strong>
<span>01-18 11:00 ~ 02-27 03:59 (11天4小时56分钟后结束)</span>
</div>
</div>
<div
class="cal-item type-character li-idx-1 li-col1"
style="margin-left:0.0%;width:100.0%"
data-id="3427"
data-type="character">
<div class="banner" style="background-image:url('https://webstatic.mihoyo.com/upload/ann/2022/12/28/fdae43817589c40f9116aee1bc08f429_773289207622522746.jpg')"></div>
<div class="info">
<img src="" class="character-img"/>
<strong>胡桃</strong>
<span>02-07 18:00 ~ 02-28 14:59 (12天15小时56分钟后结束)</span>
</div>
</div>
<div
class="cal-item type-normal li-col"
style="margin-left:0.0%;width:100.0%"
data-id="3469"
data-type="normal">
<div class="info">
<img src='https://uploadstatic.mihoyo.com/announcement/2020/03/05/f3016cc0dbe3f9c2305566742ae5927f_1830032474842461374.png' class="cal-icon"/>
<strong>【有奖活动】「磬弦奏华夜」3.4版本攻略征集活动</strong>
<span>01-18 18:00 ~ 02-26 23:59 (11天55分钟后结束)</span>
</div>
</div>
</div>
<div class="now-line" style="left:53.5461451299444%"></div>
<div class="now-line line2" style="left:53.5461451299444%"></div>
</div>
<div class="now-time">
<span>当前时间2023-02-15 23:03</span>
</div>
<div class="copyright">Inspired By Miao-Plugin</div>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB