feat: 字母描红页样式开发基本完成
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -5,6 +5,20 @@
|
||||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "47291112",
|
||||
"name": "home-o",
|
||||
"font_class": "home-o",
|
||||
"unicode": "e621",
|
||||
"unicode_decimal": 58913
|
||||
},
|
||||
{
|
||||
"icon_id": "47291111",
|
||||
"name": "home",
|
||||
"font_class": "home",
|
||||
"unicode": "e622",
|
||||
"unicode_decimal": 58914
|
||||
},
|
||||
{
|
||||
"icon_id": "47282109",
|
||||
"name": "setting",
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
import { PAPER_SIZE } from '../constants/colors';
|
||||
import { downloadPrint } from '../utils/downloadPrint';
|
||||
import { BaseDrawService } from '../service/baseDraw';
|
||||
import { BaseDrawService } from '../core/draw/baseDraw';
|
||||
import tracker from '../utils/tracker';
|
||||
import { defaultShareConfig } from '../config/config';
|
||||
|
||||
|
||||
@@ -32,6 +32,10 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.nav-bar__back--hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.nav-bar__back-icon {
|
||||
font-size: 40rpx;
|
||||
color: @color-nav-title;
|
||||
@@ -57,4 +61,4 @@
|
||||
.nav-bar__placeholder {
|
||||
width: 100%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import { NAV_INNER_PX } from '../../utils/navMetrics';
|
||||
|
||||
const HOME_TAB_URL = '/pages/home/home';
|
||||
const TAB_BAR_ROUTES = new Set([
|
||||
'pages/home/home',
|
||||
'pages/age/age',
|
||||
'pages/favorites/favorites',
|
||||
'pages/profile/profile',
|
||||
]);
|
||||
|
||||
Component({
|
||||
options: { multipleSlots: true },
|
||||
properties: {
|
||||
@@ -9,26 +17,53 @@ Component({
|
||||
data: {
|
||||
statusBarHeight: 0,
|
||||
navBlockHeight: 0,
|
||||
showBackButton: false,
|
||||
leftActionType: 'none',
|
||||
},
|
||||
lifetimes: {
|
||||
attached() {
|
||||
const { statusBarHeight } = wx.getWindowInfo();
|
||||
const pages = getCurrentPages();
|
||||
this.setData({
|
||||
statusBarHeight,
|
||||
navBlockHeight: statusBarHeight + NAV_INNER_PX,
|
||||
showBackButton: pages.length > 1,
|
||||
});
|
||||
this.syncLeftAction();
|
||||
},
|
||||
},
|
||||
pageLifetimes: {
|
||||
show() {
|
||||
this.syncLeftAction();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleBack() {
|
||||
wx.navigateBack({
|
||||
fail: () => {
|
||||
wx.switchTab({ url: '/pages/home/home' });
|
||||
},
|
||||
});
|
||||
syncLeftAction() {
|
||||
const pages = getCurrentPages();
|
||||
const currentRoute = pages[pages.length - 1]?.route || '';
|
||||
const isTabBarPage = TAB_BAR_ROUTES.has(currentRoute);
|
||||
|
||||
let leftActionType: 'none' | 'back' | 'home' = 'none';
|
||||
|
||||
if (pages.length > 1) {
|
||||
leftActionType = 'back';
|
||||
} else if (!isTabBarPage) {
|
||||
leftActionType = 'home';
|
||||
}
|
||||
|
||||
this.setData({ leftActionType });
|
||||
},
|
||||
|
||||
handleLeftAction() {
|
||||
if (this.data.leftActionType === 'back') {
|
||||
wx.navigateBack({
|
||||
fail: () => {
|
||||
wx.switchTab({ url: HOME_TAB_URL });
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.data.leftActionType === 'home') {
|
||||
wx.switchTab({ url: HOME_TAB_URL });
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
<view class="nav-bar" style="padding-top: {{statusBarHeight}}px; {{background ? 'background:' + background + ';' : ''}}">
|
||||
<view
|
||||
class="nav-bar"
|
||||
style="padding-top: {{statusBarHeight}}px; {{background ? 'background:' + background + ';' : ''}}">
|
||||
<view class="nav-bar__inner">
|
||||
<view
|
||||
wx:if="{{showBackButton}}"
|
||||
class="nav-bar__back"
|
||||
bindtap="handleBack">
|
||||
<text class="toy-icon toy-icon-arrow-left nav-bar__back-icon"></text>
|
||||
class="nav-bar__back {{leftActionType === 'none' ? 'nav-bar__back--hidden' : ''}}"
|
||||
bindtap="handleLeftAction">
|
||||
<text
|
||||
wx:if="{{leftActionType === 'back'}}"
|
||||
class="toy-icon toy-icon-arrow-left nav-bar__back-icon"></text>
|
||||
<text
|
||||
wx:elif="{{leftActionType === 'home'}}"
|
||||
class="toy-icon toy-icon-home-o nav-bar__back-icon"></text>
|
||||
</view>
|
||||
<slot name="left"></slot>
|
||||
<text class="nav-bar__title">{{title}}</text>
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
padding: 0 24rpx;
|
||||
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
|
||||
// padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
box-sizing: border-box;
|
||||
z-index: 60;
|
||||
|
||||
@@ -22,23 +23,27 @@
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
/* flex 子项:占宽比例;min-width:0 避免子项溢出时把 flex 算崩 */
|
||||
&__secondary {
|
||||
flex: 4;
|
||||
flex: 3.92;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__primary {
|
||||
flex: 6;
|
||||
flex: 6.18;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__secondary .toy-btn,
|
||||
&__primary .toy-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
/*
|
||||
* 以下类通过 toy-button 的 custom-class 作用在内部原生 button 上。
|
||||
* 不能写 &__primary .toy-btn:小程序样式无法穿透进子自定义组件。
|
||||
*/
|
||||
.preview-footer-actions__toy-btn {
|
||||
border-radius: 28rpx;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
&__primary .toy-btn--primary {
|
||||
.preview-footer-actions__toy-btn--primary {
|
||||
box-shadow: 0 4rpx 12rpx rgba(255, 215, 0, 0.3);
|
||||
}
|
||||
}
|
||||
@@ -9,12 +9,21 @@ Component({
|
||||
properties: {
|
||||
primaryText: {
|
||||
type: String,
|
||||
value: '保存到作品库',
|
||||
value: '保存到相册',
|
||||
},
|
||||
primaryIcon: {
|
||||
type: String,
|
||||
value: 'download',
|
||||
},
|
||||
secondaryText: {
|
||||
type: String,
|
||||
value: '分享',
|
||||
},
|
||||
/** 须对应 iconfont 中 toy-icon-{name};历史上常用 wechat 已映射为 wechat-o */
|
||||
secondaryIcon: {
|
||||
type: String,
|
||||
value: 'line-share',
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
@@ -28,7 +37,31 @@ Component({
|
||||
value: 'share',
|
||||
},
|
||||
},
|
||||
data: {
|
||||
/** 经归一化后的图标名,供 wxml 绑定(避免父级传 wechat 等 iconfont 中不存在的 name) */
|
||||
resolvedSecondaryIcon: 'line-share',
|
||||
},
|
||||
observers: {
|
||||
secondaryIcon() {
|
||||
this.syncSecondaryIconGlyph();
|
||||
},
|
||||
},
|
||||
lifetimes: {
|
||||
attached() {
|
||||
this.syncSecondaryIconGlyph();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
syncSecondaryIconGlyph() {
|
||||
const raw = (this.data.secondaryIcon ?? '').trim();
|
||||
const aliases: Record<string, string> = {
|
||||
wechat: 'wechat-o',
|
||||
};
|
||||
const name = aliases[raw] || raw;
|
||||
this.setData({
|
||||
resolvedSecondaryIcon: name || 'line-share',
|
||||
});
|
||||
},
|
||||
onPrimaryTap() {
|
||||
if (this.data.disabled || this.data.loading) return;
|
||||
this.triggerEvent('primary');
|
||||
|
||||
@@ -1,23 +1,33 @@
|
||||
<view class="preview-footer-actions">
|
||||
<view class="preview-footer-actions__bar">
|
||||
<toy-button
|
||||
class="preview-footer-actions__secondary"
|
||||
type="secondary"
|
||||
icon="line-share"
|
||||
icon-size="40rpx"
|
||||
open-type="{{ shareOpenType }}"
|
||||
bind:click="onSecondaryTap">
|
||||
{{ secondaryText }}
|
||||
</toy-button>
|
||||
<toy-button
|
||||
class="preview-footer-actions__primary"
|
||||
type="primary"
|
||||
icon="download"
|
||||
icon-size="40rpx"
|
||||
disabled="{{ disabled }}"
|
||||
loading="{{ loading }}"
|
||||
bind:click="onPrimaryTap">
|
||||
{{ primaryText }}
|
||||
</toy-button>
|
||||
<view class="preview-footer-actions__secondary">
|
||||
<toy-button
|
||||
type="secondary"
|
||||
icon="{{ resolvedSecondaryIcon }}"
|
||||
icon-size="40rpx"
|
||||
block
|
||||
width="100%"
|
||||
height="88rpx"
|
||||
open-type="{{ shareOpenType }}"
|
||||
custom-class="preview-footer-actions__toy-btn"
|
||||
bind:click="onSecondaryTap">
|
||||
{{ secondaryText }}
|
||||
</toy-button>
|
||||
</view>
|
||||
<view class="preview-footer-actions__primary">
|
||||
<toy-button
|
||||
type="primary"
|
||||
icon="{{ primaryIcon }}"
|
||||
icon-size="40rpx"
|
||||
block
|
||||
width="100%"
|
||||
height="88rpx"
|
||||
disabled="{{ disabled }}"
|
||||
loading="{{ loading }}"
|
||||
custom-class="preview-footer-actions__toy-btn preview-footer-actions__toy-btn--primary"
|
||||
bind:click="onPrimaryTap">
|
||||
{{ primaryText }}
|
||||
</toy-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PAPER_SIZE } from '../constants/colors';
|
||||
import { PAPER_SIZE } from '../../constants/colors';
|
||||
import { drawBaseHeader, drawBaseMiniHeader } from './baseHeaderDraw';
|
||||
|
||||
/**
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getMiniCodeImage, getImage } from '../utils/index';
|
||||
import { getMiniCodeImage, getImage } from '../../utils/index';
|
||||
|
||||
/**
|
||||
* 绘制数学模块页眉的参数接口(尺寸除以3)
|
||||
@@ -10,7 +10,6 @@
|
||||
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
|
||||
"draw-ad": "../../components/draw-ad/draw-ad",
|
||||
"preview-footer-actions": "../../components3.0/preview-footer-actions/preview-footer-actions",
|
||||
"toy-button": "../../toy/button/button",
|
||||
"toy-icon": "../../toy/icon/icon"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,19 +15,20 @@ page {
|
||||
padding-top: 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: @section-gap;
|
||||
/* space-y-8 = 32px → 64rpx */
|
||||
gap: 64rpx;
|
||||
}
|
||||
|
||||
/* ===== 预览卡 ===== */
|
||||
/* ===== 预览卡 (rounded-xl shadow-sm p-8 border) ===== */
|
||||
.lt-preview-wrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.lt-preview-card {
|
||||
background: @bg-white;
|
||||
border-radius: @radius;
|
||||
border-radius: @radius-xl;
|
||||
box-shadow: @shadow;
|
||||
padding: 48rpx @page-padding-inner-x 32rpx;
|
||||
padding: 48rpx;
|
||||
border: @border;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
@@ -38,12 +39,14 @@ page {
|
||||
.lt-preview-kicker {
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-size: 22rpx;
|
||||
font-weight: 700;
|
||||
color: @text-gray;
|
||||
/* text-xs = 12px → 24rpx */
|
||||
font-size: @fs-section-head-title;
|
||||
font-weight: bold;
|
||||
color: @text-secondary;
|
||||
// 设置字母间距,使文本字符之间有更宽的间隔,提升可读性和美观性
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 24rpx;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.lt-canvas-wrap {
|
||||
@@ -63,118 +66,170 @@ page {
|
||||
box-shadow: @shadow;
|
||||
}
|
||||
|
||||
/* ===== 参数面板 ===== */
|
||||
/* ===== 参数面板 (bg-surface-container rounded-xl p-6 space-y-6) ===== */
|
||||
.lt-params {
|
||||
background: @bg-header;
|
||||
border-radius: @radius;
|
||||
padding: 40rpx @page-padding-inner-x;
|
||||
/* surface-container = #f0e7d6 */
|
||||
background: @bg-card;
|
||||
border-radius: @radius-xl;
|
||||
padding: 48rpx;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 40rpx;
|
||||
border: @border;
|
||||
gap: 48rpx;
|
||||
box-shadow: @shadow;
|
||||
}
|
||||
|
||||
/* 标题行 (gap-2 = 8px → 16rpx) */
|
||||
.lt-params-head {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.lt-params-emoji {
|
||||
font-size: 36rpx;
|
||||
/* text-xl = 20px → 40rpx */
|
||||
font-size: 40rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.lt-params-title {
|
||||
/* text-lg = 18px → 36rpx */
|
||||
font-size: @fs-section-head-title;
|
||||
font-weight: 700;
|
||||
color: @text-title;
|
||||
letter-spacing: @ls-nav-title;
|
||||
}
|
||||
|
||||
/* 参数区块内间距 (space-y-3 = 12px → 24rpx) */
|
||||
.param-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* space-y-3 = 12px → 24rpx */
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
/* 字段标签 (text-sm font-bold text-on-surface-variant ml-1) */
|
||||
.lt-field-label {
|
||||
display: block;
|
||||
font-size: 26rpx;
|
||||
/* text-sm = 14px → 28rpx */
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
/* on-surface-variant = #605b50 */
|
||||
color: @text-secondary;
|
||||
margin-bottom: 16rpx;
|
||||
margin-left: 4rpx;
|
||||
/* ml-1 = 4px → 8rpx */
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
|
||||
/* ===== 练习类型:2×2 网格选择器 ===== */
|
||||
/* ===== 练习类型 Tab (grid-cols-2 gap-2 bg-surface-container-high p-1.5 rounded-lg) ===== */
|
||||
.lt-mode-seg {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
/* gap-2 = 8px → 16rpx */
|
||||
gap: 16rpx;
|
||||
/* p-1.5 = 6px → 12rpx */
|
||||
padding: 12rpx;
|
||||
/* surface-container-high = #eae2d0 */
|
||||
background: #eae2d0;
|
||||
border-radius: 32rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 未选中 item (py-2.5 px-3 text-sm font-medium rounded-lg text-on-surface-variant) */
|
||||
.lt-mode-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24rpx 16rpx;
|
||||
border-radius: @radius-sm;
|
||||
background: @bg-white;
|
||||
border: 2rpx solid rgba(50, 46, 37, 0.1);
|
||||
box-shadow: 0 2rpx 8rpx rgba(50, 46, 37, 0.04);
|
||||
transition: all 0.2s ease;
|
||||
/* py-2.5 = 10px → 20rpx; px-3 = 12px → 24rpx */
|
||||
padding: 20rpx 24rpx;
|
||||
/* rounded-lg = 8px → 16rpx */
|
||||
border-radius: 32rpx;
|
||||
background: transparent;
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
// transition: all 1s ease;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 选中 item (bg-primary-container text-on-primary-container font-bold shadow-sm) */
|
||||
.lt-mode-item--active {
|
||||
/* primary-container = #ffd709 */
|
||||
background: @brand;
|
||||
border-color: rgba(255, 215, 9, 0.55);
|
||||
box-shadow: @shadow;
|
||||
box-shadow: 0 2rpx 8rpx rgba(50, 46, 37, 0.08);
|
||||
}
|
||||
|
||||
.lt-mode-item__text {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: @text-secondary;
|
||||
line-height: 1.35;
|
||||
/* text-sm = 14px → 28rpx */
|
||||
font-size: 28rpx;
|
||||
/* 未选中 font-medium = 500 */
|
||||
font-weight: 500;
|
||||
/* on-surface-variant = #605b50 */
|
||||
color: @text-secondary; //#5b4b00
|
||||
line-height: 40rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 选中态字色 (on-primary-container = #5b4b00, font-bold = 700) */
|
||||
.lt-mode-item--active .lt-mode-item__text {
|
||||
/* on-primary-container = #5b4b00 */
|
||||
color: @text-selected-btn;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* ===== 字母选择网格:7 列 ===== */
|
||||
/* ===== 字母选择网格 (grid-cols-6 gap-2) ===== */
|
||||
.lt-letter-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
gap: 12rpx;
|
||||
/* 稿 grid-cols-6 */
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
/* gap-2 = 8px → 16rpx */
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
/* 字母格子 (aspect-square rounded-lg bg-surface-container-lowest) */
|
||||
.lt-letter-cell {
|
||||
aspect-ratio: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: @radius-sm;
|
||||
border-radius: 32rpx;
|
||||
/* surface-container-lowest = #ffffff */
|
||||
background: @bg-white;
|
||||
border: 2rpx solid rgba(50, 46, 37, 0.1);
|
||||
box-shadow: 0 2rpx 8rpx rgba(50, 46, 37, 0.04);
|
||||
transition: all 0.15s ease;
|
||||
box-sizing: border-box;
|
||||
/* 不用 :active:小程序 view 上背景色常在松手后才刷新;用 hover-class 才能按下即变色,并做过渡 */
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
transform 0.2s ease,
|
||||
box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
/* 按下态(hover-class):与选中态同色,松手后由 --active 保持 */
|
||||
.lt-letter-cell--pressed {
|
||||
transform: scale(0.95);
|
||||
background: @brand;
|
||||
box-shadow: 0 2rpx 8rpx rgba(50, 46, 37, 0.08);
|
||||
}
|
||||
|
||||
/* 选中字母 (bg-[#FFD700] text-[#6c5a00] shadow-sm) */
|
||||
.lt-letter-cell--active {
|
||||
background: @brand;
|
||||
border-color: rgba(255, 215, 9, 0.55);
|
||||
box-shadow: @shadow;
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 2rpx 8rpx rgba(50, 46, 37, 0.08);
|
||||
// box-shadow: @btn-shadow-pressed;
|
||||
}
|
||||
|
||||
.lt-letter-text {
|
||||
font-size: 32rpx;
|
||||
/* text-lg = 18px → 36rpx */
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
/* on-surface-variant = #605b50 */
|
||||
color: @text-secondary;
|
||||
line-height: 1;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.lt-letter-cell--active .lt-letter-text {
|
||||
/* #6c5a00 */
|
||||
color: @text-selected-btn;
|
||||
}
|
||||
|
||||
.lt-letter-cell--pressed .lt-letter-text {
|
||||
color: @text-selected-btn;
|
||||
}
|
||||
|
||||
@@ -182,36 +237,90 @@ page {
|
||||
.lt-chip-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
/* gap-2 = 8px → 16rpx */
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
/* 未选中 chip (同练习类型 item 风格) */
|
||||
.lt-chip {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 24rpx 20rpx;
|
||||
/* py-2.5 px-3 */
|
||||
padding: 20rpx 24rpx;
|
||||
/* text-sm = 14px → 28rpx */
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
line-height: 40rpx;
|
||||
color: @text-secondary;
|
||||
background: @bg-white;
|
||||
border-radius: @radius-sm;
|
||||
border: 2rpx solid rgba(50, 46, 37, 0.1);
|
||||
box-shadow: 0 2rpx 8rpx rgba(50, 46, 37, 0.04);
|
||||
background: #fff;
|
||||
/* rounded-lg */
|
||||
border-radius: 32rpx;
|
||||
transition:
|
||||
background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
transform 0.2s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
color 0.2s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
/* 按下态(hover-class),未选与已选 chip 共用 */
|
||||
.lt-chip--pressed {
|
||||
transform: scale(0.96);
|
||||
background: @brand;
|
||||
color: @text-selected-btn;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 2rpx 8rpx rgba(50, 46, 37, 0.08);
|
||||
}
|
||||
|
||||
/* 选中 chip (同练习类型选中态) */
|
||||
.lt-chip--active {
|
||||
background: @brand;
|
||||
color: @text-selected-btn;
|
||||
font-weight: 700;
|
||||
border-color: rgba(255, 215, 9, 0.55);
|
||||
box-shadow: @shadow;
|
||||
box-shadow: 0 2rpx 8rpx rgba(50, 46, 37, 0.08);
|
||||
}
|
||||
|
||||
/* ===== 换一批(toy-button outlined 覆盖) ===== */
|
||||
/* 换一批:自定义描边按钮(对齐 stitch 粗边框 + 图标文案横排,避免 toy-button 覆盖成本) */
|
||||
.lt-shuffle-btn {
|
||||
border-width: 6rpx !important;
|
||||
border-color: @bg-header !important;
|
||||
height: 96rpx !important;
|
||||
font-size: 32rpx !important;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 108rpx;
|
||||
padding: 0 32rpx;
|
||||
border-radius: 32rpx;
|
||||
border: 8rpx solid @bg-card;
|
||||
background: transparent;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12rpx;
|
||||
/* 与字母格/chip 一致:view 上勿依赖 :active 做背景或稳定缩放,用 hover-class */
|
||||
transition:
|
||||
transform 0.2s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
/* 稿 active:scale-[0.98],按下时略铺底便于看出过渡 */
|
||||
.lt-shuffle-btn--hover {
|
||||
transform: scale(0.98);
|
||||
opacity: 0.92;
|
||||
background-color: rgba(234, 226, 208, 0.55);
|
||||
}
|
||||
|
||||
/* toy-icon 自定义组件宿主节点默认 display:inline,须强制为 inline-flex 方可参与 flex 对齐 */
|
||||
.lt-shuffle-btn toy-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.lt-shuffle-btn__text {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
color: @text-secondary;
|
||||
transition: color 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.lt-spacer-bottom {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import LetterTracingDraw from '../shared/service/letterTracingDraw';
|
||||
import LetterTracingDraw from '../shared/draw/letterTracingDraw';
|
||||
import { createPage, type CanvasDataState } from '../../base/pageMixin';
|
||||
import { defaultShareConfig } from '../../config/config';
|
||||
import { getWorksheetById } from '../../core/data/worksheets';
|
||||
@@ -81,36 +81,15 @@ createPage(
|
||||
options.id && PAGE_META[options.id]
|
||||
? options.id
|
||||
: 'letter-tracing-single';
|
||||
const def = getWorksheetById(worksheetId);
|
||||
const base = (def?.generatorConfig ??
|
||||
{}) as Partial<LetterTracingGeneratorConfig>;
|
||||
const merged = mergeLetterTracingConfig(base, {});
|
||||
|
||||
const traceMode = merged.mode;
|
||||
const showLetterPicker = traceMode === 'single-letter';
|
||||
const showCaseToggle =
|
||||
traceMode === 'full-alphabet' || traceMode === 'two-column';
|
||||
const showNextLetter = traceMode === 'single-letter';
|
||||
|
||||
this.setData({
|
||||
worksheetId,
|
||||
functionId: worksheetId,
|
||||
traceMode,
|
||||
selectedLetter: normalizePick(merged.selectedLetter),
|
||||
letterCaseLower: merged.letterCase === 'lower',
|
||||
showLetterPicker,
|
||||
showCaseToggle,
|
||||
showNextLetter,
|
||||
});
|
||||
|
||||
this.initPageInfo(worksheetId, PAGE_META[worksheetId]?.title ?? '字母描红');
|
||||
this.applyWorksheet(worksheetId);
|
||||
},
|
||||
|
||||
onSelectMode(e: WechatMiniprogram.TouchEvent) {
|
||||
const id = e.currentTarget.dataset.id as string | undefined;
|
||||
if (!id || id === this.data.worksheetId) return;
|
||||
wx.redirectTo({
|
||||
url: `/englishPages/letterTracing/letterTracing?id=${id}`,
|
||||
this.applyWorksheet(id, {
|
||||
preserveLetter: true,
|
||||
redraw: true,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -184,6 +163,61 @@ createPage(
|
||||
this.setData({ selectedLetter: next });
|
||||
this.drawCanvas();
|
||||
},
|
||||
|
||||
applyWorksheet(
|
||||
worksheetId: string,
|
||||
options?: {
|
||||
preserveLetter?: boolean;
|
||||
redraw?: boolean;
|
||||
},
|
||||
) {
|
||||
const def = getWorksheetById(worksheetId);
|
||||
const base = (def?.generatorConfig ??
|
||||
{}) as Partial<LetterTracingGeneratorConfig>;
|
||||
const merged = mergeLetterTracingConfig(base, {});
|
||||
|
||||
const traceMode = merged.mode;
|
||||
const showLetterPicker = traceMode === 'single-letter';
|
||||
const showCaseToggle =
|
||||
traceMode === 'full-alphabet' || traceMode === 'two-column';
|
||||
const showNextLetter = traceMode === 'single-letter';
|
||||
const selectedLetter = options?.preserveLetter
|
||||
? this.data.selectedLetter
|
||||
: normalizePick(merged.selectedLetter);
|
||||
const letterCaseLower =
|
||||
traceMode === 'single-letter'
|
||||
? merged.letterCase === 'lower'
|
||||
: this.data.letterCaseLower;
|
||||
|
||||
this.setData(
|
||||
{
|
||||
worksheetId,
|
||||
functionId: worksheetId,
|
||||
traceMode,
|
||||
selectedLetter,
|
||||
letterCaseLower,
|
||||
showLetterPicker,
|
||||
showCaseToggle,
|
||||
showNextLetter,
|
||||
},
|
||||
() => {
|
||||
this.initPageInfo(
|
||||
worksheetId,
|
||||
PAGE_META[worksheetId]?.title ?? '字母描红',
|
||||
);
|
||||
|
||||
if (this.drawService) {
|
||||
this.drawService.options.title = this.data.pageTitle;
|
||||
this.drawService.options.subTitle =
|
||||
this.data.subTitle || '';
|
||||
}
|
||||
|
||||
if (options?.redraw) {
|
||||
this.drawCanvas();
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
shareConfig: defaultShareConfig,
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
wx:key="*this"
|
||||
class="lt-letter-cell {{selectedLetter === item ? 'lt-letter-cell--active' : ''}}"
|
||||
data-letter="{{item}}"
|
||||
hover-class="lt-letter-cell--pressed"
|
||||
hover-start-time="0"
|
||||
hover-stay-time="70"
|
||||
bind:tap="onSelectLetter">
|
||||
<text class="lt-letter-text">{{item}}</text>
|
||||
</view>
|
||||
@@ -54,11 +57,17 @@
|
||||
<view class="lt-chip-row">
|
||||
<view
|
||||
class="lt-chip {{!letterCaseLower ? 'lt-chip--active' : ''}}"
|
||||
hover-class="lt-chip--pressed"
|
||||
hover-start-time="0"
|
||||
hover-stay-time="70"
|
||||
bind:tap="onSelectCaseUpper">
|
||||
大写 A–Z
|
||||
</view>
|
||||
<view
|
||||
class="lt-chip {{letterCaseLower ? 'lt-chip--active' : ''}}"
|
||||
hover-class="lt-chip--pressed"
|
||||
hover-start-time="0"
|
||||
hover-stay-time="70"
|
||||
bind:tap="onSelectCaseLower">
|
||||
小写 a–z
|
||||
</view>
|
||||
@@ -66,27 +75,25 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<toy-button
|
||||
<view
|
||||
wx:if="{{showNextLetter}}"
|
||||
type="default"
|
||||
variant="outlined"
|
||||
block
|
||||
icon="refresh"
|
||||
custom-class="lt-shuffle-btn"
|
||||
bind:click="onNextLetter">
|
||||
换一批
|
||||
</toy-button>
|
||||
|
||||
class="lt-shuffle-btn"
|
||||
hover-class="lt-shuffle-btn--hover"
|
||||
hover-start-time="0"
|
||||
hover-stay-time="70"
|
||||
bindtap="onNextLetter">
|
||||
<toy-icon
|
||||
name="refresh"
|
||||
size="40rpx"
|
||||
color="#605b50"
|
||||
custom-class="lt-shuffle-btn__icon" />
|
||||
<text class="lt-shuffle-btn__text">换一批</text>
|
||||
</view>
|
||||
<draw-ad type="mathDraw"></draw-ad>
|
||||
<view class="lt-spacer-bottom"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<preview-footer-actions
|
||||
primary-text="保存到相册"
|
||||
primary-icon="download"
|
||||
secondary-text="分享"
|
||||
secondary-icon="wechat"
|
||||
disabled="{{!hasContent}}"
|
||||
bind:primary="exportToPrint"
|
||||
bind:secondary="onShare" />
|
||||
|
||||
+17
-3
@@ -1,4 +1,4 @@
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
import type {
|
||||
LetterTracingData,
|
||||
TracingRow,
|
||||
@@ -169,7 +169,13 @@ export default class LetterTracingDraw extends BaseDrawService {
|
||||
const rowH = Math.max(36, (rest - 8) / rows.length);
|
||||
const rowWidth = cw - M * 2;
|
||||
for (let r = 0; r < rows.length; r++) {
|
||||
this.drawTracingRow(rows[r], M, y + r * (rowH + 10), rowH, rowWidth);
|
||||
this.drawTracingRow(
|
||||
rows[r],
|
||||
M,
|
||||
y + r * (rowH + 10),
|
||||
rowH,
|
||||
rowWidth,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +218,15 @@ export default class LetterTracingDraw extends BaseDrawService {
|
||||
if (idx >= chars.length) break;
|
||||
const cx = M + c * (cellW + 4);
|
||||
this.drawFourLines(cx, y, cellW, cellH);
|
||||
this.drawCharInCell(chars[idx], cx, y, cellW, cellH, 0.85, true);
|
||||
this.drawCharInCell(
|
||||
chars[idx],
|
||||
cx,
|
||||
y,
|
||||
cellW,
|
||||
cellH,
|
||||
0.85,
|
||||
true,
|
||||
);
|
||||
}
|
||||
y += cellH + 6;
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
* 译码连线绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
|
||||
/**
|
||||
* 译码连线数据
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 颜色找规律绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
import {
|
||||
drawShapeSymbolShape,
|
||||
DrawShapeOptions,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 根据颜色画图形绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
import { drawShapeSymbolShape } from '../shapes/shapeSymbolShapes';
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 数字点连线绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
|
||||
/**
|
||||
* 数字点连线数据
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* 合并了基础绘制和内容绘制逻辑
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
import { GridCell, GridConfig } from '../types/gridTypes';
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 方格推理绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
|
||||
/**
|
||||
* 3x3网格中的位置坐标
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 线条识别绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
import { getImage } from '../../../utils/index';
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 连连看绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
import { getImage } from '../../../utils/index';
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 方位涂涂乐绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
import { getImage } from '../../../utils/index';
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 图形符号配对绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
import { drawShapeSymbolShape } from '../shapes/shapeSymbolShapes';
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 加减法计算绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
import { getImage } from '../../../utils/index';
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 破十法练习绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
|
||||
/**
|
||||
* 破十法数据
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 计算练习题绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
|
||||
/**
|
||||
* 计算练习题数据
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 数一数比大小绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
import { getImage } from '../../../utils/index';
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 数一数连一连绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
import { getImage } from '../../../utils/index';
|
||||
import { getRandomNumberColor } from '../../../constants/colors';
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* 支持两种模式:'select'(选一选)和 'fill'(填一填)
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
import { getImage } from '../../../utils/index';
|
||||
import { getRandomNumberColor } from '../../../constants/colors';
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 平十法练习绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
|
||||
/**
|
||||
* 平十法数据
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 凑十法练习绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
|
||||
/**
|
||||
* 凑十法数据
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 填上缺少的数字绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
|
||||
/**
|
||||
* 填上缺少的数字数据
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 九九乘法表绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
import { getRandomUniqueNumberColors } from '../../../constants/colors';
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 按数字涂颜色绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
import { getNumberColors } from '../../../constants/colors';
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 10以内数的分与合绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
import { getImage } from '../../../utils/index';
|
||||
import { getRandomNumberColor } from '../../../constants/colors';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
import { drawNumberPreview } from './numberPreviewDraw';
|
||||
import { drawNumberWriteContent } from './numberWriteDraw';
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 数物对应绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
import { getImage } from '../../../utils/index';
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 数字排序绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
|
||||
/**
|
||||
* 数字排序数据
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 一位数加法绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||||
import { getRandomNumberColor } from '../../../constants/colors';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BaseDrawService } from './baseDraw';
|
||||
import { BaseDrawService } from '../core/draw/baseDraw';
|
||||
import { POSITION_TEMPLATES } from './findWordTemplate';
|
||||
|
||||
// ==================== Debug 开关 ====================
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BaseDrawService } from './baseDraw';
|
||||
import { BaseDrawService } from '../core/draw/baseDraw';
|
||||
import { ShapeCard } from '../constants/shapes';
|
||||
import { drawShape } from './drawShape';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BaseDrawService } from './baseDraw';
|
||||
import { BaseDrawService } from '../core/draw/baseDraw';
|
||||
/**
|
||||
* 计算示例区域圆的中心点
|
||||
* @param canvasWidth 画布宽度(逻辑像素)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BaseDrawService } from './baseDraw';
|
||||
import { BaseDrawService } from '../core/draw/baseDraw';
|
||||
import { CharacterItem } from '../types/characterType';
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
@bg-gray: #f5edd8;
|
||||
@bg-muted: #f5f5f5; // 分龄 Tab 未选中等弱对比底(Stitch)
|
||||
@bg-week-head: #fafafa; // 分龄周卡头部(Stitch stone-50)
|
||||
@bg-card: #f0e7d6;
|
||||
|
||||
// ========== 文本色 ==========
|
||||
@text-title: #322e25;
|
||||
|
||||
@@ -3,21 +3,30 @@
|
||||
/* ── design tokens(严格对齐 stitch-ui/buttonUi/code.html 色谱) ── */
|
||||
|
||||
/* primary 色系 */
|
||||
@btn-primary-solid: #ffd709; /* primary-container / primary-fixed */
|
||||
@btn-primary-gradient-from: #ffd709; /* primary-fixed */
|
||||
@btn-primary-gradient-to: #efc900; /* primary-fixed-dim */
|
||||
@btn-primary-text: #5b4b00; /* on-primary-container */
|
||||
@btn-primary-pressed-bg: #5e4e00; /* primary-dim */
|
||||
@btn-primary-pressed-text: #fff2cd; /* on-primary */
|
||||
@btn-primary-solid: #ffd709;
|
||||
/* primary-container / primary-fixed */
|
||||
@btn-primary-gradient-from: #ffd709;
|
||||
/* primary-fixed */
|
||||
@btn-primary-gradient-to: #efc900;
|
||||
/* primary-fixed-dim */
|
||||
@btn-primary-text: #5b4b00;
|
||||
/* on-primary-container */
|
||||
@btn-primary-pressed-bg: #5e4e00;
|
||||
/* primary-dim */
|
||||
@btn-primary-pressed-text: #fff2cd;
|
||||
/* on-primary */
|
||||
@btn-primary-ring: rgba(108, 90, 0, 0.10);
|
||||
|
||||
/* secondary 色系 */
|
||||
@btn-secondary-bg: #9cd343; /* tertiary-container / tertiary-fixed */
|
||||
@btn-secondary-bg: #9cd343;
|
||||
/* tertiary-container / tertiary-fixed */
|
||||
@btn-secondary-text-on-tint: #5a8c1a;
|
||||
|
||||
/* error 色系 */
|
||||
@btn-error-bg: #f95630; /* error-container */
|
||||
@btn-error-text: #520c00; /* on-error-container */
|
||||
@btn-error-bg: #f95630;
|
||||
/* error-container */
|
||||
@btn-error-text: #520c00;
|
||||
/* on-error-container */
|
||||
|
||||
/* surface 参考 */
|
||||
@btn-surface-variant: #e5dcc9;
|
||||
@@ -45,8 +54,8 @@
|
||||
@btn-disabled-bg: @bg-gray;
|
||||
@btn-disabled-color: @text-disable;
|
||||
|
||||
/* ── reset native button chrome ── */
|
||||
wx-button:not([size=mini]) {
|
||||
/* ── reset native button chrome(全宽块级按钮由 .toy-btn--block 自行控制宽度) ── */
|
||||
wx-button.toy-btn:not([size=mini]):not(.toy-btn--block) {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: auto;
|
||||
@@ -252,17 +261,21 @@ wx-button:not([size=mini]) {
|
||||
}
|
||||
}
|
||||
|
||||
/* ═══════ block ═══════ */
|
||||
/* ═══════ block(铺满父级 flex 槽位,避免被 wx-button 默认 width:auto 限制) ═══════ */
|
||||
&--block {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* ═══════ states ═══════ */
|
||||
|
||||
/* 通用按下态:缩放 0.95 */
|
||||
/* 通用按下态:缩放 0.9 */
|
||||
&--active {
|
||||
transform: scale(0.95);
|
||||
transform: scale(0.9);
|
||||
box-shadow: @btn-shadow-pressed;
|
||||
}
|
||||
|
||||
@@ -312,6 +325,11 @@ wx-button:not([size=mini]) {
|
||||
}
|
||||
|
||||
@keyframes toy-btn-spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@@ -171,6 +171,7 @@ Component({
|
||||
visualType,
|
||||
variant,
|
||||
disabled,
|
||||
pressed,
|
||||
);
|
||||
const resolvedIconSize =
|
||||
this.normalizeUnit(iconSize) ||
|
||||
@@ -214,10 +215,16 @@ Component({
|
||||
type: string,
|
||||
variant: string,
|
||||
disabled: boolean,
|
||||
pressed: boolean,
|
||||
) {
|
||||
if (disabled) return '#b8b2a6';
|
||||
if (iconColor) return iconColor;
|
||||
|
||||
/* 与 .toy-btn--primary.toy-btn--active 的 color(@btn-primary-pressed-text)一致 */
|
||||
if (pressed && variant === 'filled' && type === 'primary') {
|
||||
return '#fff2cd';
|
||||
}
|
||||
|
||||
if (variant === 'outlined' || variant === 'ghost') {
|
||||
const outlinedMap: Record<string, string> = {
|
||||
primary: '#6c5a00',
|
||||
|
||||
@@ -24,9 +24,13 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 内层 text 显示 iconfont 字形 */
|
||||
/* 内层 text 承载 ::before 字形,固定 1em 方盒便于与 flex 行内文案垂直对齐 */
|
||||
> text {
|
||||
display: inline-block;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
line-height: 1;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@@ -66,10 +66,8 @@ Component({
|
||||
const styleSegments = [style];
|
||||
if (normalizedSize) {
|
||||
styleSegments.push(`font-size:${normalizedSize}`);
|
||||
if (isImage) {
|
||||
styleSegments.push(`width:${normalizedSize}`);
|
||||
styleSegments.push(`height:${normalizedSize}`);
|
||||
}
|
||||
styleSegments.push(`width:${normalizedSize}`);
|
||||
styleSegments.push(`height:${normalizedSize}`);
|
||||
}
|
||||
if (color && !isImage) {
|
||||
styleSegments.push(`color:${color}`);
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# Design System Strategy: Doodle Mini
|
||||
|
||||
## 1. Overview & Creative North Star: "The Sun-Drenched Studio"
|
||||
The creative direction for this design system is **"The Sun-Drenched Studio."** We are moving away from the cold, clinical "educational app" look and toward a space that feels like a physical, tactile craft room bathed in morning light.
|
||||
|
||||
Instead of a rigid, boxed-in grid, we embrace **Organic Asymmetry**. Elements should feel like they were placed on a table by hand, not snapped to a pixel-perfect wireframe. We break the "template" look by using exaggerated corner radii, overlapping "paper" layers, and a hierarchy that prioritizes warmth and play over traditional data density.
|
||||
|
||||
## 2. Colors: Tonal Radiance
|
||||
The palette is anchored by `primary` (#6c5a00) and `primary_container` (#ffd709), creating a "Yaya Yellow" experience that is vibrant yet sophisticated.
|
||||
|
||||
* **The "No-Line" Rule:** Under no circumstances should 1px solid borders be used to separate content. We define boundaries exclusively through background shifts. Use `surface_container_low` (#f8f0e0) for main content areas and `surface` (#fef6e7) for the global backdrop.
|
||||
* **Surface Hierarchy & Nesting:** Treat the UI as stacked sheets of heavy-weight construction paper.
|
||||
* **Level 0:** `background` (#fef6e7) – The desk surface.
|
||||
* **Level 1:** `surface_container` (#f0e7d6) – The worksheet base.
|
||||
* **Level 2:** `surface_container_lowest` (#ffffff) – Active drawing cards or "Paper" modals.
|
||||
* **The Glass & Gradient Rule:** For navigation bars or floating tool palettes, use Glassmorphism. Apply `surface_container_low` at 80% opacity with a `20px` backdrop blur. This ensures the "sunny" background bleeds through, maintaining the "Sun-Drenched" atmosphere.
|
||||
* **Signature Textures:** Main CTAs should not be flat. Apply a subtle linear gradient from `primary_fixed` (#ffd709) to `primary_fixed_dim` (#efc900) at a 145-degree angle to give buttons a "pillowy" 3D volume.
|
||||
|
||||
## 3. Typography: The Friendly Guide
|
||||
We use **Plus Jakarta Sans** exclusively. Its geometric yet soft terminals provide a modern, legible, and approachable voice.
|
||||
|
||||
* **Display & Headlines:** Use `display-lg` (3.5rem) for "Doodle Mini" brand moments. Headlines (`headline-md`) should use a tighter letter-spacing (-0.02em) to feel "hugged" and friendly.
|
||||
* **Body & Labels:** Use `body-lg` for all instructional text. In a children's context, readability is paramount; ensure a line height of at least 1.6x the font size.
|
||||
* **Hierarchy as Personality:** Use `on_secondary_container` (#6d3b00) for sub-headers to provide a warm, "chocolatey" contrast against the yellow, moving away from harsh blacks.
|
||||
|
||||
## 4. Elevation & Depth: Tonal Layering
|
||||
Depth in this system is a result of light and shadow, not lines and strokes.
|
||||
|
||||
* **The Layering Principle:** To lift a component, place a `surface_container_highest` (#e5dcc9) element inside a `surface_container_low` (#f8f0e0) environment. This creates a "recessed" or "elevated" feel through color value alone.
|
||||
* **Ambient Shadows:** Use shadows sparingly. When an object "floats" (like a floating action button), use a shadow color derived from `on_surface` (#322e25) at 6% opacity, with a blur of `32px` and a Y-offset of `8px`. It should look like a soft glow, not a drop shadow.
|
||||
* **The "Ghost Border" Fallback:** If a separator is required for accessibility in input fields, use `outline_variant` (#b3ac9f) at **15% opacity**. It should be felt, not seen.
|
||||
|
||||
## 5. Components: Tactile & Safe
|
||||
Every component must feel "squishy" and safe to touch.
|
||||
|
||||
* **Buttons:**
|
||||
* **Primary:** Uses `primary_container` (#ffd709) with a `full` (9999px) radius.
|
||||
* **States:** On press, scale the button down to 96% size to simulate a physical "click" into the screen.
|
||||
* **Cards & Lists:** Forbid divider lines. Separate list items using a `spacing-3` (1rem) gap and alternating background colors between `surface_container_low` and `surface_container`.
|
||||
* **Input Fields:** Use `surface_container_highest` for the input track. Corners must be `xl` (3rem). Labels sit *above* the field in `title-sm` to ensure children can always see the context.
|
||||
* **Selection Chips:** Use `secondary_container` (#ffc697) for selected states. The roundedness must be `full` to mimic smooth river stones.
|
||||
* **The "Joy" Progress Bar:** A custom component for Doodle Mini. Use a thick track (`1.5rem` height) in `surface_container_highest` with the progress indicator using a gradient of `tertiary` (#006b1b) to `tertiary_fixed` (#91f78e).
|
||||
|
||||
## 6. Do’s and Don’ts
|
||||
|
||||
### Do:
|
||||
* **Do** use `xl` and `full` corner radii for almost everything. Sharp corners are "scary."
|
||||
* **Do** lean into white space. Use `spacing-12` (4rem) between major sections to let the design breathe.
|
||||
* **Do** use `on_tertiary_container` (dark green) for success states; it feels organic and "earthy" against the yellow.
|
||||
|
||||
### Don’t:
|
||||
* **Don’t** use pure black (#000000). Use `on_surface` (#322e25) for all dark text to maintain the warm, sunny tone.
|
||||
* **Don’t** use 1px dividers. If you feel you need one, use a `surface_variant` (#e5dcc9) color block that is `4px` thick with `full` rounded caps.
|
||||
* **Don’t** crowd the interface. If a screen feels busy, increase the background "paper" size and use a scrollable layout instead of shrinking elements.
|
||||
@@ -0,0 +1,239 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="zh-CN"><head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet"/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||
<script id="tailwind-config">
|
||||
tailwind.config = {
|
||||
darkMode: "class",
|
||||
theme: {
|
||||
extend: {
|
||||
"colors": {
|
||||
"outline-variant": "#b3ac9f",
|
||||
"tertiary-container": "#91f78e",
|
||||
"on-tertiary": "#d1ffc8",
|
||||
"on-error": "#ffefec",
|
||||
"surface-container-high": "#eae2d0",
|
||||
"inverse-surface": "#110e06",
|
||||
"surface-container-lowest": "#ffffff",
|
||||
"on-surface": "#322e25",
|
||||
"surface-variant": "#e5dcc9",
|
||||
"secondary-fixed": "#ffc697",
|
||||
"tertiary-fixed": "#91f78e",
|
||||
"secondary-dim": "#794200",
|
||||
"tertiary-fixed-dim": "#83e881",
|
||||
"surface-dim": "#dcd4c0",
|
||||
"on-primary": "#fff2cd",
|
||||
"primary-dim": "#5e4e00",
|
||||
"surface": "#fef6e7",
|
||||
"secondary-fixed-dim": "#ffb470",
|
||||
"surface-container-low": "#f8f0e0",
|
||||
"error-container": "#f95630",
|
||||
"inverse-on-surface": "#a29c8f",
|
||||
"error": "#b02500",
|
||||
"outline": "#7c766a",
|
||||
"on-primary-fixed": "#453900",
|
||||
"background": "#fef6e7",
|
||||
"on-secondary-fixed-variant": "#7a4300",
|
||||
"tertiary-dim": "#005d16",
|
||||
"secondary-container": "#ffc697",
|
||||
"on-tertiary-fixed": "#00480f",
|
||||
"primary": "#6c5a00",
|
||||
"on-secondary-fixed": "#512a00",
|
||||
"on-error-container": "#520c00",
|
||||
"surface-container": "#f0e7d6",
|
||||
"on-primary-fixed-variant": "#665500",
|
||||
"on-secondary-container": "#6d3b00",
|
||||
"on-secondary": "#fff0e6",
|
||||
"inverse-primary": "#ffd709",
|
||||
"primary-container": "#ffd709",
|
||||
"surface-bright": "#fef6e7",
|
||||
"primary-fixed": "#ffd709",
|
||||
"tertiary": "#006b1b",
|
||||
"on-tertiary-container": "#005e17",
|
||||
"primary-fixed-dim": "#efc900",
|
||||
"error-dim": "#b92902",
|
||||
"surface-tint": "#6c5a00",
|
||||
"on-surface-variant": "#605b50",
|
||||
"on-tertiary-fixed-variant": "#00691a",
|
||||
"secondary": "#8a4c00",
|
||||
"on-background": "#322e25",
|
||||
"surface-container-highest": "#e5dcc9",
|
||||
"on-primary-container": "#5b4b00"
|
||||
},
|
||||
"borderRadius": {
|
||||
"DEFAULT": "1rem",
|
||||
"lg": "2rem",
|
||||
"xl": "3rem",
|
||||
"full": "9999px"
|
||||
},
|
||||
"fontFamily": {
|
||||
"headline": ["Plus Jakarta Sans"],
|
||||
"body": ["Plus Jakarta Sans"],
|
||||
"label": ["Plus Jakarta Sans"]
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
@media print {
|
||||
.no-print { display: none; }
|
||||
body { background: white; }
|
||||
.a4-page {
|
||||
box-shadow: none !important;
|
||||
margin: 0 !important;
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
.material-symbols-outlined {
|
||||
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
body {
|
||||
min-height: max(884px, 100dvh);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-surface-container-low font-body text-on-surface antialiased min-h-screen flex flex-col items-center py-10">
|
||||
<!-- TopAppBar Shell (Shared Component) -->
|
||||
<header class="w-full pt-8 pb-4 bg-[#fef6e7] dark:bg-stone-950 border-b-4 border-[#e5dcc9] dark:border-stone-800 rounded-b-none no-print">
|
||||
<div class="max-w-[210mm] mx-auto px-12 flex flex-row items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="material-symbols-outlined text-[#6c5a00] text-3xl" data-icon="draw">draw</span>
|
||||
<h1 class="text-2xl font-bold text-[#6c5a00] dark:text-yellow-500 font-['Plus_Jakarta_Sans'] tracking-tight leading-[1.6]">Doodle Mini</h1>
|
||||
</div>
|
||||
<div class="flex items-center gap-6">
|
||||
<nav class="hidden md:flex items-center gap-8">
|
||||
<a class="text-[#6c5a00] dark:text-yellow-400 font-bold font-['Plus_Jakarta_Sans']" href="#">Studio</a>
|
||||
<a class="text-[#322e25] dark:text-stone-400 hover:bg-[#f0e7d6] dark:hover:bg-stone-800 transition-transform duration-200 p-2 rounded-lg font-['Plus_Jakarta_Sans']" href="#">Templates</a>
|
||||
<a class="text-[#322e25] dark:text-stone-400 hover:bg-[#f0e7d6] dark:hover:bg-stone-800 transition-transform duration-200 p-2 rounded-lg font-['Plus_Jakarta_Sans']" href="#">Gallery</a>
|
||||
</nav>
|
||||
<div class="bg-primary-container text-on-primary-container px-4 py-1.5 rounded-full text-sm font-bold active:scale-95 transition-all cursor-pointer">
|
||||
ID: #8821
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<!-- Main Canvas (A4 Paper) -->
|
||||
<main class="a4-page bg-surface-container-lowest w-[210mm] min-h-[297mm] shadow-[0_20px_50px_rgba(0,0,0,0.05)] mt-12 p-12 relative overflow-hidden flex flex-col">
|
||||
<!-- Worksheet Header Section -->
|
||||
<div class="border-b-4 border-on-surface/5 pb-8 mb-12">
|
||||
<!-- Top Section -->
|
||||
<div class="flex justify-between items-start mb-10">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="w-16 h-16 bg-primary-container rounded-xl flex items-center justify-center rotate-3">
|
||||
<img alt="Doodle Mini Logo" class="w-12 h-12" data-alt="minimalist playful kid drawing logo with brush stroke yellow and brown colors on a clean white background" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBdSOaW7wfndomKJ5FM9GgaqBCTAJXenyZWLXZDNDtt2PjtkDd-Ndlw5qes3qbkNfmDVVb4ovEYTvOG9U7Y5oA4HzAk0diN9fMxamhc5RV-TzkkbM1QmmC4qJ3xFn4GhxY6mwChzKp3o8i3BGpINikxJpcO5ZznuhL4ZuyrrlkzEBR2teL-Cke62jO1E7BTm4W9lB0JoH2kMePNVobUQYzgcqE_SEvgXbcn5yZmoevDnMgC37cdjm5aAIyVW11dNEaBM7j0Isek1y4"/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-xl font-black text-primary tracking-tighter">Doodle Mini</div>
|
||||
<div class="text-[10px] text-outline font-bold tracking-widest uppercase">Educational Studio</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="bg-surface-container px-3 py-1 rounded-lg text-[11px] font-mono font-bold text-on-surface-variant">MATH-ADD-10-01</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Middle Section (Title) -->
|
||||
<div class="text-center mb-10">
|
||||
<h2 class="text-5xl font-black text-on-surface tracking-tight mb-2">10以内加法</h2>
|
||||
<div class="h-1.5 w-24 bg-primary-container mx-auto rounded-full"></div>
|
||||
</div>
|
||||
<!-- Bottom Section (Fields) -->
|
||||
<div class="flex justify-between items-end px-4">
|
||||
<div class="flex items-end gap-2 flex-1">
|
||||
<span class="text-lg font-bold text-on-surface-variant whitespace-nowrap">姓名:</span>
|
||||
<div class="border-b-2 border-surface-container-highest flex-1 h-8 mb-1"></div>
|
||||
</div>
|
||||
<div class="flex items-end gap-2 flex-1 mx-8">
|
||||
<span class="text-lg font-bold text-on-surface-variant whitespace-nowrap">日期:</span>
|
||||
<div class="border-b-2 border-surface-container-highest flex-1 h-8 mb-1"></div>
|
||||
</div>
|
||||
<div class="flex items-end gap-2 w-32">
|
||||
<span class="text-lg font-bold text-on-surface-variant whitespace-nowrap">得分:</span>
|
||||
<div class="border-b-2 border-surface-container-highest flex-1 h-8 mb-1"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Math Problems Content Area (Bento Preview) -->
|
||||
<div class="grid grid-cols-2 gap-x-16 gap-y-10 flex-1">
|
||||
<!-- Left Column Problems -->
|
||||
<div class="space-y-12">
|
||||
<div class="flex items-center justify-between text-3xl font-bold tracking-widest text-on-surface/80">
|
||||
<span>3 + 4 =</span>
|
||||
<div class="w-24 h-16 border-2 border-surface-container-highest rounded-2xl bg-surface-container-low/30"></div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between text-3xl font-bold tracking-widest text-on-surface/80">
|
||||
<span>5 + 2 =</span>
|
||||
<div class="w-24 h-16 border-2 border-surface-container-highest rounded-2xl bg-surface-container-low/30"></div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between text-3xl font-bold tracking-widest text-on-surface/80">
|
||||
<span>1 + 8 =</span>
|
||||
<div class="w-24 h-16 border-2 border-surface-container-highest rounded-2xl bg-surface-container-low/30"></div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between text-3xl font-bold tracking-widest text-on-surface/80">
|
||||
<span>6 + 3 =</span>
|
||||
<div class="w-24 h-16 border-2 border-surface-container-highest rounded-2xl bg-surface-container-low/30"></div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between text-3xl font-bold tracking-widest text-on-surface/80">
|
||||
<span>2 + 7 =</span>
|
||||
<div class="w-24 h-16 border-2 border-surface-container-highest rounded-2xl bg-surface-container-low/30"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Right Column Problems -->
|
||||
<div class="space-y-12">
|
||||
<div class="flex items-center justify-between text-3xl font-bold tracking-widest text-on-surface/80">
|
||||
<span>4 + 4 =</span>
|
||||
<div class="w-24 h-16 border-2 border-surface-container-highest rounded-2xl bg-surface-container-low/30"></div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between text-3xl font-bold tracking-widest text-on-surface/80">
|
||||
<span>0 + 9 =</span>
|
||||
<div class="w-24 h-16 border-2 border-surface-container-highest rounded-2xl bg-surface-container-low/30"></div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between text-3xl font-bold tracking-widest text-on-surface/80">
|
||||
<span>7 + 1 =</span>
|
||||
<div class="w-24 h-16 border-2 border-surface-container-highest rounded-2xl bg-surface-container-low/30"></div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between text-3xl font-bold tracking-widest text-on-surface/80">
|
||||
<span>3 + 5 =</span>
|
||||
<div class="w-24 h-16 border-2 border-surface-container-highest rounded-2xl bg-surface-container-low/30"></div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between text-3xl font-bold tracking-widest text-on-surface/80">
|
||||
<span>5 + 5 =</span>
|
||||
<div class="w-24 h-16 border-2 border-surface-container-highest rounded-2xl bg-surface-container-low/30"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Visual Decoration for Kids (Not visible in printing ideally but adds to UI) -->
|
||||
<div class="absolute bottom-8 right-12 opacity-10 grayscale no-print">
|
||||
<span class="material-symbols-outlined text-8xl" data-icon="rocket_launch">rocket_launch</span>
|
||||
</div>
|
||||
<div class="absolute bottom-8 left-12 opacity-10 grayscale no-print">
|
||||
<span class="material-symbols-outlined text-8xl" data-icon="auto_awesome">auto_awesome</span>
|
||||
</div>
|
||||
</main>
|
||||
<!-- BottomNavBar Shell (Shared Component) -->
|
||||
<footer class="fixed bottom-0 left-0 w-full z-50 flex justify-around items-center px-6 pb-6 pt-2 print:hidden bg-[#f8f0e0]/80 backdrop-blur-xl shadow-[0_-8px_32px_rgba(50,46,37,0.06)] rounded-t-[3rem]">
|
||||
<button class="flex flex-col items-center justify-center bg-[#ffd709] text-[#6c5a00] rounded-full p-3 transition-transform active:scale-96">
|
||||
<span class="material-symbols-outlined" data-icon="brush">brush</span>
|
||||
<span class="font-['Plus_Jakarta_Sans'] text-xs font-bold">Studio</span>
|
||||
</button>
|
||||
<button class="flex flex-col items-center justify-center text-[#322e25] p-3 hover:opacity-80 transition-opacity active:scale-96">
|
||||
<span class="material-symbols-outlined" data-icon="description">description</span>
|
||||
<span class="font-['Plus_Jakarta_Sans'] text-xs font-bold">Templates</span>
|
||||
</button>
|
||||
<button class="flex flex-col items-center justify-center text-[#322e25] p-3 hover:opacity-80 transition-opacity active:scale-96">
|
||||
<span class="material-symbols-outlined" data-icon="auto_awesome_motion">auto_awesome_motion</span>
|
||||
<span class="font-['Plus_Jakarta_Sans'] text-xs font-bold">Gallery</span>
|
||||
</button>
|
||||
</footer>
|
||||
<!-- FAB for Print Action -->
|
||||
<button class="no-print fixed right-8 bottom-28 w-16 h-16 bg-primary-container text-on-primary-container rounded-full shadow-lg flex items-center justify-center active:scale-95 transition-all hover:brightness-105 z-[60]" onclick="window.print()">
|
||||
<span class="material-symbols-outlined text-3xl" data-icon="print">print</span>
|
||||
</button>
|
||||
</body></html>
|
||||
@@ -0,0 +1 @@
|
||||
<FIFE Image failed to fetch>
|
||||
Reference in New Issue
Block a user