feat: 字母描红页样式开发基本完成
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user