小程序渲染问题
This commit is contained in:
1 parent
88bffaa5d0
commit
919da19be7
5 files changed
+194
-101
No files matched your search
+2
-2
@@ -66,8 +66,8 @@ Page({
|
||||
a: '在 Node 里用 @napi-rs/canvas 跨 item 文件夹读取资源渲染首帧,尺寸 400×400,右下角为 Spine 官方 badge,左下角是完整版本号。移除这个依赖只影响缩略图,画面预览不受影响。',
|
||||
},
|
||||
{
|
||||
q: '试用版和插件是什么关系?',
|
||||
a: '试用版只支持 Spine 4.2 导出的文件,用来快速看一眼效果;3.4–4.3 全版本识别、皮肤切换、时间轴、缩略图这些完整能力都在桌面插件里。',
|
||||
q: '小程序版和插件是什么关系?',
|
||||
a: '小程序版支持 Spine 4.2 导出的文件,用来在手机上快速看一眼效果;3.4–4.3 全版本识别、皮肤切换、时间轴、缩略图这些完整能力都在桌面插件里。',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
<view class="hero-cta">
|
||||
<view class="btn primary" bindtap="goBuy">查看套餐</view>
|
||||
<view class="btn ghost" bindtap="goPreview">试用版体验</view>
|
||||
<view class="btn ghost" bindtap="goPreview">小程序版体验</view>
|
||||
</view>
|
||||
|
||||
<view class="stats">
|
||||
@@ -29,9 +29,9 @@
|
||||
|
||||
<!-- ===== 小程序定位 ===== -->
|
||||
<view class="notice">
|
||||
<view class="notice-title">这里是试用版</view>
|
||||
<view class="notice-desc">试用版只支持 Spine 4.2 导出的文件;桌面插件的完整能力包括 3.4–4.3 全版本识别、皮肤切换、时间轴、缩略图生成。</view>
|
||||
<view class="notice-link" bindtap="goPreview">去试用版看看 →</view>
|
||||
<view class="notice-title">小程序版</view>
|
||||
<view class="notice-desc">小程序版支持 Spine 4.2 导出的文件,可以先在手机上看看效果;桌面插件的完整能力包括 3.4–4.3 全版本识别、皮肤切换、时间轴、缩略图生成。</view>
|
||||
<view class="notice-link" bindtap="goPreview">去小程序版看看 →</view>
|
||||
</view>
|
||||
|
||||
<!-- ===== 功能特性 ===== -->
|
||||
|
||||
+27
-10
@@ -43,6 +43,7 @@ Page({
|
||||
_ty: 0,
|
||||
_fitOffset: null,
|
||||
_fitSize: null,
|
||||
_lastSizeCheck: 0,
|
||||
_lastTime: 0,
|
||||
_raf: null,
|
||||
_ready: false,
|
||||
@@ -80,10 +81,15 @@ Page({
|
||||
wx.createSelectorQuery()
|
||||
.in(this)
|
||||
.select('#spineCanvas')
|
||||
.fields({ node: true, size: true })
|
||||
.fields({ node: true })
|
||||
// 画布节点返回的 size 是缓冲区尺寸(不是当前布局尺寸),
|
||||
// 所以量外面这层普通 view 来定缓冲区大小,否则布局一变画面就会被拉伸
|
||||
.select('#stageBox')
|
||||
.fields({ size: true })
|
||||
.exec((res) => {
|
||||
const hit = res && res[0]
|
||||
if (!hit || !hit.node) {
|
||||
const canvasHit = res && res[0]
|
||||
const stageHit = res && res[1]
|
||||
if (!canvasHit || !canvasHit.node) {
|
||||
this.setData({
|
||||
status: 'error',
|
||||
errorMsg: '取不到 canvas 节点,请确认基础库支持 <canvas type="webgl">',
|
||||
@@ -91,8 +97,8 @@ Page({
|
||||
return
|
||||
}
|
||||
|
||||
const canvas = hit.node
|
||||
const size = hit.size || { width: 300, height: 300 }
|
||||
const canvas = canvasHit.node
|
||||
const size = (stageHit && stageHit.size) || { width: 300, height: 300 }
|
||||
|
||||
let dpr = 1
|
||||
try {
|
||||
@@ -158,11 +164,11 @@ Page({
|
||||
if (!this._canvas || !this._gl) return
|
||||
wx.createSelectorQuery()
|
||||
.in(this)
|
||||
.select('#spineCanvas')
|
||||
.fields({ node: true, size: true })
|
||||
.select('#stageBox')
|
||||
.fields({ size: true })
|
||||
.exec((res) => {
|
||||
const hit = res && res[0]
|
||||
if (!hit || !hit.node || !hit.size) return
|
||||
if (!hit || !hit.size) return
|
||||
const size = hit.size
|
||||
if (!size.width || !size.height) return
|
||||
|
||||
@@ -470,6 +476,15 @@ Page({
|
||||
if (!canvas || !gl || !this._skeleton) return
|
||||
|
||||
const now = Date.now()
|
||||
|
||||
// 面板内容变化(比如加载后多出播放/参数区)会改变画布显示尺寸。
|
||||
// 只在加载后校对一次是不够的:那次可能跑在布局生效之前,之后就再也不会纠正,
|
||||
// 缓冲区与显示区比例不一致就会让画面被拉伸。这里定期校对,尺寸没变时直接返回,开销很小。
|
||||
if (now - this._lastSizeCheck > 400) {
|
||||
this._lastSizeCheck = now
|
||||
this.syncCanvasSize()
|
||||
}
|
||||
|
||||
let dt = (now - this._lastTime) / 1000
|
||||
this._lastTime = now
|
||||
if (!(dt > 0)) dt = 0
|
||||
@@ -487,8 +502,10 @@ Page({
|
||||
const mvp = env.buildMVP(scale, this._tx, this._ty, W, H)
|
||||
|
||||
gl.viewport(0, 0, W, H)
|
||||
// 深色画布背景(与 UI 的深色预览区保持一致)
|
||||
gl.clearColor(0.12, 0.13, 0.16, 1)
|
||||
// 透明清屏:canvas 是原生组件、层级在 WebView 之上,
|
||||
// 用不透明颜色清屏会盖住压在它上面的浮层(缩放按钮、手势提示、信息徽标)。
|
||||
// 背景交给舞台的 CSS 渐变,观感一致,但浮层能正常显示。
|
||||
gl.clearColor(0, 0, 0, 0)
|
||||
gl.clear(gl.COLOR_BUFFER_BIT)
|
||||
// 预乘开关:同时影响渲染器的混色方式与 GL 混合函数
|
||||
const pma = this.data.premultiplied
|
||||
|
||||
+32
-26
@@ -1,7 +1,7 @@
|
||||
<view class="page">
|
||||
|
||||
<!-- ===== 预览区 ===== -->
|
||||
<view class="stage">
|
||||
<view class="stage" id="stageBox">
|
||||
<canvas
|
||||
id="spineCanvas"
|
||||
class="stage-canvas"
|
||||
@@ -35,17 +35,24 @@
|
||||
|
||||
<!-- 右上角信息徽标 -->
|
||||
<view wx:if="{{status === 'ready'}}" class="stage-badge">{{info}}</view>
|
||||
|
||||
</view>
|
||||
|
||||
<!-- ===== 控制面板 ===== -->
|
||||
<view class="sheet">
|
||||
<!-- 文件 -->
|
||||
<view class="section">
|
||||
<button class="pick-btn" bindtap="chooseFiles">
|
||||
<text class="pick-text">{{pickedCount ? '重新选择文件' : '选择文件'}}</text>
|
||||
</button>
|
||||
<view class="pick-sub">可一次选中多个文件 · .skel / .atlas / .png</view>
|
||||
<!-- 画布下方:手势提示 + 缩放 -->
|
||||
<view wx:if="{{status === 'ready'}}" class="tool-row">
|
||||
<view class="tool-hint">单指拖动平移 · 双指捏合缩放</view>
|
||||
<view class="zoom-group">
|
||||
<view class="zoom-btn" bindtap="zoomOut">-</view>
|
||||
<text class="zoom-val">{{scalePercent}}%</text>
|
||||
<view class="zoom-btn" bindtap="zoomIn">+</view>
|
||||
<view class="zoom-reset" bindtap="resetView">重置</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 已选文件 -->
|
||||
<view wx:if="{{pickedCount}}" class="section">
|
||||
<view wx:if="{{pickedCount}}" class="chips">
|
||||
<view wx:for="{{pickedFiles}}" wx:key="id" class="chip chip-{{item.kind}}">
|
||||
<text class="chip-dot"></text>
|
||||
@@ -55,19 +62,19 @@
|
||||
|
||||
<view wx:if="{{missingText}}" class="missing">{{missingText}}</view>
|
||||
|
||||
<view wx:if="{{pickedCount}}" class="link" bindtap="clearFiles">清空已选</view>
|
||||
</view>
|
||||
|
||||
<!-- 播放 -->
|
||||
<view wx:if="{{animations.length}}" class="section">
|
||||
<view class="section-title">播放</view>
|
||||
|
||||
<picker mode="selector" range="{{animations}}" value="{{animIndex}}" bindchange="onAnimChange">
|
||||
<view class="select">
|
||||
<text class="select-label">{{animations[animIndex]}}</text>
|
||||
<text class="select-arrow">▾</text>
|
||||
</view>
|
||||
</picker>
|
||||
<view class="section-head">
|
||||
<view class="section-title">播放</view>
|
||||
<picker class="anim-picker" mode="selector" range="{{animations}}" value="{{animIndex}}" bindchange="onAnimChange">
|
||||
<view class="select">
|
||||
<text class="select-label">{{animations[animIndex]}}</text>
|
||||
<text class="select-arrow">▾</text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="segment">
|
||||
<view class="seg {{playing ? 'on' : ''}}" bindtap="togglePlay">{{playing ? '⏸ 暂停' : '▶ 播放'}}</view>
|
||||
@@ -90,18 +97,17 @@
|
||||
/>
|
||||
<text class="param-val">{{speed}}x</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="param">
|
||||
<text class="param-name">缩放</text>
|
||||
<view class="stepper">
|
||||
<view class="step-btn" bindtap="zoomOut">-</view>
|
||||
<text class="step-val">{{scalePercent}}%</text>
|
||||
<view class="step-btn" bindtap="zoomIn">+</view>
|
||||
</view>
|
||||
<view class="link inline" bindtap="resetView">重置</view>
|
||||
<!-- 选择文件:固定放在最下面,随时能重新选 -->
|
||||
<view class="section pick-section">
|
||||
<view class="pick-row">
|
||||
<view wx:if="{{pickedCount}}" class="clear-btn" bindtap="clearFiles">清空已选</view>
|
||||
<button class="pick-btn" bindtap="chooseFiles">
|
||||
<text class="pick-text">{{pickedCount ? '重新选择文件' : '选择文件'}}</text>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<view class="gesture-hint">单指拖动平移 · 双指捏合缩放</view>
|
||||
<view class="pick-sub">可一次选中多个文件 · .skel / .atlas / .png</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
+129
-59
@@ -9,9 +9,12 @@
|
||||
/* ===== 预览区(深色)===== */
|
||||
.stage {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
background: linear-gradient(180deg, var(--panel) 0%, #14161b 100%);
|
||||
/* 固定高度:控制面板出现/消失都不再改变画布的显示区域,
|
||||
否则缓冲区比例会和显示区对不上,画面会被拉伸 */
|
||||
flex: none;
|
||||
height: 50vh;
|
||||
/* 和整页统一底色,不要再上下两种颜色 */
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
.stage-canvas {
|
||||
@@ -71,17 +74,75 @@
|
||||
top: 22rpx;
|
||||
padding: 8rpx 18rpx;
|
||||
border-radius: var(--r-pill);
|
||||
background: rgba(0, 0, 0, 0.34);
|
||||
color: rgba(255, 255, 255, 0.72);
|
||||
background: #24242c;
|
||||
color: rgba(255, 255, 255, 0.82);
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
/* ===== 画布下方:手势提示 + 缩放控制 ===== */
|
||||
.tool-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.tool-hint {
|
||||
flex: 1;
|
||||
margin-right: 16rpx;
|
||||
font-size: 21rpx;
|
||||
color: rgba(255, 255, 255, 0.42);
|
||||
}
|
||||
|
||||
.zoom-group {
|
||||
flex: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4rpx;
|
||||
border-radius: var(--r-pill);
|
||||
background: var(--panel-2);
|
||||
}
|
||||
|
||||
.zoom-btn {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
line-height: 56rpx;
|
||||
text-align: center;
|
||||
font-size: 30rpx;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
}
|
||||
|
||||
.zoom-val {
|
||||
min-width: 92rpx;
|
||||
text-align: center;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.zoom-reset {
|
||||
height: 56rpx;
|
||||
line-height: 56rpx;
|
||||
padding: 0 20rpx;
|
||||
margin-left: 6rpx;
|
||||
border-radius: var(--r-pill);
|
||||
background: #34343e;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
/* ===== 控制面板(深色,和预览区统一)===== */
|
||||
.sheet {
|
||||
background: var(--bg-2);
|
||||
border-radius: var(--r-lg) var(--r-lg) 0 0;
|
||||
/* 同上:整页一种底色,去掉面板的圆角与投影,避免出现分界线 */
|
||||
background: var(--bg);
|
||||
/* 面板自己滚动,这样它的内容变化不会挤动上面的画布 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
padding: 30rpx 28rpx 28rpx;
|
||||
box-shadow: 0 -10rpx 36rpx rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
.section {
|
||||
@@ -131,6 +192,36 @@
|
||||
color: rgba(255, 255, 255, 0.38);
|
||||
}
|
||||
|
||||
/* 选择文件放在面板最下面 */
|
||||
.pick-section {
|
||||
/* auto 让它在内容不足时贴到面板底部,内容多时自然跟在最后 */
|
||||
margin-top: auto;
|
||||
padding-top: 26rpx;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* 清空已选 + 重新选择文件:两个等宽按钮并排 */
|
||||
.pick-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pick-row .pick-btn {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.clear-btn {
|
||||
flex: 1;
|
||||
height: 100rpx;
|
||||
line-height: 100rpx;
|
||||
margin-right: 20rpx;
|
||||
text-align: center;
|
||||
border-radius: var(--r-lg);
|
||||
background: var(--panel-2);
|
||||
color: rgba(255, 255, 255, 0.72);
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
/* ===== 文件类型标签 ===== */
|
||||
.chips {
|
||||
display: flex;
|
||||
@@ -186,18 +277,6 @@
|
||||
color: #f0b45c;
|
||||
}
|
||||
|
||||
.link {
|
||||
margin-top: 14rpx;
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.45);
|
||||
}
|
||||
|
||||
.link.inline {
|
||||
margin-top: 0;
|
||||
margin-left: 18rpx;
|
||||
color: var(--cyan);
|
||||
}
|
||||
|
||||
/* ===== 播放选择 ===== */
|
||||
.select {
|
||||
display: flex;
|
||||
@@ -221,6 +300,35 @@
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
/* ===== 播放(标题和动画列表同一行,开关在下面一行)===== */
|
||||
.section-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 14rpx;
|
||||
}
|
||||
|
||||
.section-head .section-title {
|
||||
flex: none;
|
||||
margin-right: 20rpx;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.anim-picker {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.section-head .select {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* 动画名太长时省略,不要挤掉右边的按钮 */
|
||||
.select-label {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* ===== 分段控件 ===== */
|
||||
.segment {
|
||||
display: flex;
|
||||
@@ -241,7 +349,7 @@
|
||||
|
||||
.seg.on {
|
||||
background: #3d262e;
|
||||
color: var(--cyan);
|
||||
color: var(--accent-2);
|
||||
font-weight: 600;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
@@ -273,41 +381,3 @@
|
||||
font-weight: 600;
|
||||
color: var(--cyan);
|
||||
}
|
||||
|
||||
/* ===== 缩放步进器 ===== */
|
||||
.stepper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 16rpx;
|
||||
background: var(--panel-2);
|
||||
border-radius: var(--r-lg);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.step-btn {
|
||||
width: 70rpx;
|
||||
height: 64rpx;
|
||||
line-height: 64rpx;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
}
|
||||
|
||||
.step-btn:active {
|
||||
background: var(--panel-2);
|
||||
}
|
||||
|
||||
.step-val {
|
||||
min-width: 104rpx;
|
||||
text-align: center;
|
||||
font-size: 25rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.gesture-hint {
|
||||
margin-top: 4rpx;
|
||||
text-align: center;
|
||||
font-size: 22rpx;
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
Reference in new issue
Block a user