显卡完善

This commit is contained in:
1 2025-07-25 19:55:12 +08:00
parent 2355147ed5
commit 7236a95236

View File

@ -333,17 +333,17 @@
$t('device.running-status.866086-12') }}</el-link> $t('device.running-status.866086-12') }}</el-link>
</template> </template>
<el-descriptions-item :label="$t('device.running-status.866086-13')">{{ firmware.firmwareName <el-descriptions-item :label="$t('device.running-status.866086-13')">{{ firmware.firmwareName
}}</el-descriptions-item> }}</el-descriptions-item>
<el-descriptions-item :label="$t('device.device-edit.148398-4')">{{ firmware.productName <el-descriptions-item :label="$t('device.device-edit.148398-4')">{{ firmware.productName
}}</el-descriptions-item> }}</el-descriptions-item>
<el-descriptions-item :label="$t('device.device-edit.148398-12')">Version {{ firmware.version <el-descriptions-item :label="$t('device.device-edit.148398-12')">Version {{ firmware.version
}}</el-descriptions-item> }}</el-descriptions-item>
<el-descriptions-item :label="$t('device.running-status.866086-16')"> <el-descriptions-item :label="$t('device.running-status.866086-16')">
<el-link :href="getDownloadUrl(firmware.filePath)" :underline="false" type="primary">{{ <el-link :href="getDownloadUrl(firmware.filePath)" :underline="false" type="primary">{{
getDownloadUrl(firmware.filePath) }}</el-link> getDownloadUrl(firmware.filePath) }}</el-link>
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item :label="$t('device.running-status.866086-17')">{{ firmware.remark <el-descriptions-item :label="$t('device.running-status.866086-17')">{{ firmware.remark
}}</el-descriptions-item> }}</el-descriptions-item>
</el-descriptions> </el-descriptions>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button type="success" @click="otaUpgrade" <el-button type="success" @click="otaUpgrade"
@ -561,7 +561,7 @@
<div style="text-align: right;margin-top:8px;"> <div style="text-align: right;margin-top:8px;">
<el-button @click="addProgramDialogVisible = false" size="mini"> </el-button> <el-button @click="addProgramDialogVisible = false" size="mini"> </el-button>
<el-button type="primary" @click="submitAddProgram" size="mini">{{ isEditProgram ? '更新' : '确定' <el-button type="primary" @click="submitAddProgram" size="mini">{{ isEditProgram ? '更新' : '确定'
}}</el-button> }}</el-button>
</div> </div>
</el-form> </el-form>
</el-dialog> </el-dialog>
@ -2483,6 +2483,7 @@ export default {
this.addProgramForm.zones.forEach((zone, zIdx) => { this.addProgramForm.zones.forEach((zone, zIdx) => {
const asset = this.addProgramPreviewAssets[zIdx]; const asset = this.addProgramPreviewAssets[zIdx];
if (!asset) return; if (!asset) return;
const state = this.addProgramPreviewAnimState[zIdx] || {};
// 90270x/y/width/height // 90270x/y/width/height
let zoneX, zoneY, zoneWidth, zoneHeight; let zoneX, zoneY, zoneWidth, zoneHeight;
@ -2512,12 +2513,9 @@ export default {
ctx.rect(zoneX, zoneY, zoneWidth, zoneHeight); ctx.rect(zoneX, zoneY, zoneWidth, zoneHeight);
ctx.clip(); ctx.clip();
//
const state = this.addProgramPreviewAnimState[zIdx];
if (zone.playType === 0 && asset.isText) { if (zone.playType === 0 && asset.isText) {
// // 使
const page = state && typeof state.currentPage === 'number' ? state.currentPage : 0; const page = state.currentPage || 0;
const pageLines = asset.pages[page] || []; const pageLines = asset.pages[page] || [];
// //
@ -2589,7 +2587,7 @@ export default {
ctx.fillText(line, x1, y); ctx.fillText(line, x1, y);
ctx.fillText(line, x2, y); ctx.fillText(line, x2, y);
} else { } else {
ctx.fillText(line, startX + (state ? state.currentX : 0), startY + lineIdx * lineHeight + (state ? state.currentY : 0)); ctx.fillText(line, startX + animOffsetX, startY + lineIdx * lineHeight + animOffsetY);
} }
}); });
} else if (zone.playType === 1 && asset.isImage) { } else if (zone.playType === 1 && asset.isImage) {
@ -2678,124 +2676,124 @@ export default {
}); });
}, },
prepareAddProgramPreviewAssets() { prepareAddProgramPreviewAssets() {
const canvas = this.$refs.addProgramPreviewCanvas; const canvas = this.$refs.addProgramPreviewCanvas;
if (!canvas) return; if (!canvas) return;
// //
let screenW = this.screenParams.width || 32; let screenW = this.screenParams.width || 32;
let screenH = this.screenParams.height || 64; let screenH = this.screenParams.height || 64;
// 90270 // 90270
const angle = this.screenParams.angle; const angle = this.screenParams.angle;
const swapWH = angle === 90 || angle === 270; const swapWH = angle === 90 || angle === 270;
if (swapWH) { if (swapWH) {
[screenW, screenH] = [screenH, screenW]; [screenW, screenH] = [screenH, screenW];
}
const renderWidth = 640; //
const scale = Math.min(renderWidth / screenW, 240 / screenH);
this.addProgramPreviewAssets = this.addProgramForm.zones.map(zone => {
if (zone.playType === 1 && zone.image) {
//
const img = new window.Image();
const asset = { img, isImage: true, loaded: false };
img.onload = () => {
asset.loaded = true;
this.drawAddProgramPreview();
};
img.onerror = (e) => {
console.error("Preview image failed to load:", e);
};
img.src = zone.image;
return asset;
}
if (zone.playType !== 0 || !zone.displayText) {
return { pages: [[]], isText: false };
}
//
const fontSize = parseInt(this.addProgramFontSizes[zone.fontSize] || '16px');
//
const scaledFontSize = Math.max(10, Math.round(fontSize * scale * 0.8)); // 0.8
const fontFamily = [
'SimSun, 宋体, Songti SC, serif',
'SimHei, 黑体, Heiti SC, sans-serif',
'KaiTi, 楷体, Kaiti SC, serif'
];
const fontWeight = zone.fontBold ? 'bold' : 'normal';
// canvas
const measureCanvas = document.createElement('canvas');
const measureCtx = measureCanvas.getContext('2d');
const enFontMap = [
'Courier New', 'Arial Black', 'Arial Italic', 'Lucida Console', 'Impact', 'Gothic', 'Arial Narrow', 'Comic Sans MS', 'Brush Script MT', 'Century Gothic', 'Times New Roman'
];
let fontFamilyUsed;
if (/^[A-Za-z0-9\s]+$/.test(zone.displayText)) {
fontFamilyUsed = enFontMap[zone.fontEn] || fontFamily[0];
} else {
fontFamilyUsed = fontFamily[zone.font] || fontFamily[0];
}
measureCtx.font = `${fontWeight} ${scaledFontSize}px ${fontFamilyUsed}`;
// -
let lines = [];
if (zone.effect === 5) { //
//
lines = [zone.displayText];
} else {
//
const zoneRenderWidth = swapWH ? zone.height : zone.width;
const maxWidth = zoneRenderWidth * scale - 4; // 4px
let currentLine = '';
for (const char of zone.displayText) {
const testLine = currentLine + char;
const metrics = measureCtx.measureText(testLine);
if (metrics.width > maxWidth) {
if (currentLine) lines.push(currentLine);
currentLine = char;
} else {
currentLine = testLine;
}
} }
const renderWidth = 640; //
const scale = Math.min(renderWidth / screenW, 240 / screenH);
if (currentLine) lines.push(currentLine); this.addProgramPreviewAssets = this.addProgramForm.zones.map(zone => {
} if (zone.playType === 1 && zone.image) {
//
const img = new window.Image();
const asset = { img, isImage: true, loaded: false };
img.onload = () => {
asset.loaded = true;
this.drawAddProgramPreview();
};
img.onerror = (e) => {
console.error("Preview image failed to load:", e);
};
img.src = zone.image;
return asset;
}
if (zone.playType !== 0 || !zone.displayText) {
return { pages: [[]], isText: false };
}
// //
const zoneRenderHeight = swapWH ? zone.width : zone.height; const fontSize = parseInt(this.addProgramFontSizes[zone.fontSize] || '16px');
const lineHeight = scaledFontSize * 1.2;
const maxLines = Math.max(1, Math.floor((zoneRenderHeight * scale - 4) / lineHeight));
// //
const pages = []; const scaledFontSize = Math.max(10, Math.round(fontSize * scale * 0.8)); // 0.8
for (let i = 0; i < lines.length; i += maxLines) {
pages.push(lines.slice(i, i + maxLines));
}
if (pages.length === 0) pages.push([]); const fontFamily = [
'SimSun, 宋体, Songti SC, serif',
'SimHei, 黑体, Heiti SC, sans-serif',
'KaiTi, 楷体, Kaiti SC, serif'
];
const fontWeight = zone.fontBold ? 'bold' : 'normal';
// // canvas
const totalWidth = Math.max(...pages.flat().map(line => measureCtx.measureText(line).width), 0); const measureCanvas = document.createElement('canvas');
const totalHeight = Math.min(lines.length, maxLines) * lineHeight; const measureCtx = measureCanvas.getContext('2d');
const enFontMap = [
'Courier New', 'Arial Black', 'Arial Italic', 'Lucida Console', 'Impact', 'Gothic', 'Arial Narrow', 'Comic Sans MS', 'Brush Script MT', 'Century Gothic', 'Times New Roman'
];
let fontFamilyUsed;
if (/^[A-Za-z0-9\s]+$/.test(zone.displayText)) {
fontFamilyUsed = enFontMap[zone.fontEn] || fontFamily[0];
} else {
fontFamilyUsed = fontFamily[zone.font] || fontFamily[0];
}
measureCtx.font = `${fontWeight} ${scaledFontSize}px ${fontFamilyUsed}`;
return { // -
pages, let lines = [];
isText: true, if (zone.effect === 5) { //
scaledFontSize, //
fontFamily: fontFamilyUsed, lines = [zone.displayText];
fontWeight, } else {
lineHeight, //
totalWidth, const zoneRenderWidth = swapWH ? zone.height : zone.width;
totalHeight, const maxWidth = zoneRenderWidth * scale - 4; // 4px
zoneRenderWidth: (swapWH ? zone.height : zone.width) * scale, let currentLine = '';
zoneRenderHeight: (swapWH ? zone.width : zone.height) * scale
}; for (const char of zone.displayText) {
}); const testLine = currentLine + char;
}, const metrics = measureCtx.measureText(testLine);
if (metrics.width > maxWidth) {
if (currentLine) lines.push(currentLine);
currentLine = char;
} else {
currentLine = testLine;
}
}
if (currentLine) lines.push(currentLine);
}
//
const zoneRenderHeight = swapWH ? zone.width : zone.height;
const lineHeight = scaledFontSize * 1.2;
const maxLines = Math.max(1, Math.floor((zoneRenderHeight * scale - 4) / lineHeight));
//
const pages = [];
for (let i = 0; i < lines.length; i += maxLines) {
pages.push(lines.slice(i, i + maxLines));
}
if (pages.length === 0) pages.push([]);
//
const totalWidth = Math.max(...pages.flat().map(line => measureCtx.measureText(line).width), 0);
const totalHeight = Math.min(lines.length, maxLines) * lineHeight;
return {
pages,
isText: true,
scaledFontSize,
fontFamily: fontFamilyUsed,
fontWeight,
lineHeight,
totalWidth,
totalHeight,
zoneRenderWidth: (swapWH ? zone.height : zone.width) * scale,
zoneRenderHeight: (swapWH ? zone.width : zone.height) * scale
};
});
},
resetAddProgramPreviewPage() { resetAddProgramPreviewPage() {
this.addProgramPreviewPage = this.addProgramForm.zones.map(() => 0); this.addProgramPreviewPage = this.addProgramForm.zones.map(() => 0);
}, },
@ -2864,17 +2862,20 @@ export default {
return { lines, maxLines }; return { lines, maxLines };
}, },
resetAddProgramPreviewAnimState() { resetAddProgramPreviewAnimState() {
// this.addProgramPreviewAnimState = this.addProgramForm.zones.map((zone, idx) => {
this.addProgramPreviewAnimState = this.addProgramForm.zones.map((zone, idx) => { return {
return { currentX: 0,
currentX: 0, currentY: 0,
currentY: 0, currentPage: 0,
currentPage: this.addProgramPreviewPage[idx] || 0, isMoving: false,
pageTimer: null, isPausing: false,
effect: zone.effect, moveStart: 0,
}; pauseStart: 0,
}); currentChar: 0, //
}, effect: zone.effect,
};
});
},
stopAddProgramPreviewAnimLoop() { stopAddProgramPreviewAnimLoop() {
if (this.addProgramPreviewAnimFrame) { if (this.addProgramPreviewAnimFrame) {
cancelAnimationFrame(this.addProgramPreviewAnimFrame); cancelAnimationFrame(this.addProgramPreviewAnimFrame);
@ -2891,118 +2892,233 @@ export default {
this.addProgramPreviewAnimFrame = requestAnimationFrame(loop); this.addProgramPreviewAnimFrame = requestAnimationFrame(loop);
}, },
updateAddProgramPreviewAnimState() { updateAddProgramPreviewAnimState() {
// //
const renderWidth = 640; const renderWidth = 640;
const renderHeight = 240; const renderHeight = 240;
let screenW = this.screenParams.width || 32; let screenW = this.screenParams.width || 32;
let screenH = this.screenParams.height || 64; let screenH = this.screenParams.height || 64;
const angle = this.screenParams.angle; const angle = this.screenParams.angle;
const swapWH = angle === 90 || angle === 270; const swapWH = angle === 90 || angle === 270;
if (swapWH) { if (swapWH) {
[screenW, screenH] = [screenH, screenW]; [screenW, screenH] = [screenH, screenW];
} }
const scale = Math.min(renderWidth / screenW, renderHeight / screenH); const scale = Math.min(renderWidth / screenW, renderHeight / screenH);
this.addProgramForm.zones.forEach((zone, idx) => { this.addProgramForm.zones.forEach((zone, idx) => {
const asset = this.addProgramPreviewAssets[idx]; const asset = this.addProgramPreviewAssets[idx];
const state = this.addProgramPreviewAnimState[idx]; const state = this.addProgramPreviewAnimState[idx];
if (!asset || !state || !asset.isText) return; if (!asset || !state || !asset.isText) return;
const effect = zone.effect; const effect = zone.effect;
const page = this.addProgramPreviewPage[idx] || 0; const pageLines = asset.pages[state.currentPage] || [];
const pageLines = asset.pages[page] || []; const lineHeight = asset.lineHeight;
const lineHeight = asset.lineHeight; const totalHeight = asset.totalHeight;
const totalHeight = asset.totalHeight; const totalWidth = asset.totalWidth;
const totalWidth = asset.totalWidth;
// //
const speedMap = [1, 2, 3, 4, 5]; const speedMap = [1, 2, 3, 4, 5];
const animSpeed = (speedMap[zone.speed] || 3) * scale * 0.5; // / const animSpeed = (speedMap[zone.speed] || 3) * scale * 0.5; // /
// (使) // (使)
const zoneWidth = asset.zoneRenderWidth; const zoneWidth = asset.zoneRenderWidth;
const zoneHeight = asset.zoneRenderHeight; const zoneHeight = asset.zoneRenderHeight;
// () // ()
const pauseMs = this.getPauseTime(zone.stayTime); const pauseMs = this.getPauseTime(zone.stayTime);
// //
if (typeof state.isPausing !== 'boolean') state.isPausing = false; if (typeof state.isMoving !== 'boolean') state.isMoving = false;
if (typeof state.pauseStart !== 'number') state.pauseStart = 0; if (typeof state.isPausing !== 'boolean') state.isPausing = false;
if (typeof state.pausePhase !== 'string') state.pausePhase = 'start'; // 'start' | 'move' if (typeof state.pauseStart !== 'number') state.pauseStart = 0;
if (typeof state.moveStart !== 'number') state.moveStart = 0;
switch (effect) { switch (effect) {
case 1: // case 0: //
if (typeof state.currentX !== 'number') state.currentX = zoneWidth; if (!state.isPausing && !state.isMoving) {
state.currentX -= animSpeed; //
if (state.currentX < -totalWidth) { state.isPausing = true;
if (asset.pages.length > 1) { state.pauseStart = Date.now();
state.currentPage = (state.currentPage + 1) % asset.pages.length; } else if (state.isPausing) {
} //
state.currentX = zoneWidth; if (Date.now() - state.pauseStart >= pauseMs) {
state.isPausing = false;
state.isMoving = true; //
}
} else if (state.isMoving) {
//
state.currentPage = (state.currentPage + 1) % asset.pages.length;
state.isMoving = false;
state.isPausing = true;
state.pauseStart = Date.now();
}
//
state.currentX = 0;
state.currentY = 0;
break;
case 1: //
if (!state.isMoving && !state.isPausing) {
//
state.currentX = zoneWidth;
state.isMoving = true;
state.moveStart = Date.now();
} else if (state.isMoving) {
//
const elapsed = Date.now() - state.moveStart;
const progress = Math.min(1, elapsed / (totalWidth * 1000 / animSpeed));
//
state.currentX = zoneWidth - progress * (zoneWidth + totalWidth);
//
if (progress >= 1) {
state.isMoving = false;
state.isPausing = true;
state.pauseStart = Date.now();
}
} else if (state.isPausing) {
//
if (Date.now() - state.pauseStart >= pauseMs) {
state.isPausing = false;
state.isMoving = true; //
}
} else if (!state.isPausing && state.isMoving) {
//
state.currentPage = (state.currentPage + 1) % asset.pages.length;
state.currentX = zoneWidth; //
state.isMoving = true;
state.moveStart = Date.now();
}
state.currentY = 0;
break;
case 2: //
if (!state.isMoving && !state.isPausing) {
state.currentX = -totalWidth;
state.isMoving = true;
state.moveStart = Date.now();
} else if (state.isMoving) {
const elapsed = Date.now() - state.moveStart;
const progress = Math.min(1, elapsed / (totalWidth * 1000 / animSpeed));
state.currentX = -totalWidth + progress * (zoneWidth + totalWidth);
if (progress >= 1) {
state.isMoving = false;
state.isPausing = true;
state.pauseStart = Date.now();
}
} else if (state.isPausing) {
if (Date.now() - state.pauseStart >= pauseMs) {
state.isPausing = false;
state.isMoving = true; //
}
} else if (!state.isPausing && state.isMoving) {
state.currentPage = (state.currentPage + 1) % asset.pages.length;
state.currentX = -totalWidth;
state.isMoving = true;
state.moveStart = Date.now();
}
state.currentY = 0;
break;
case 3: //
if (!state.isMoving && !state.isPausing) {
state.currentY = zoneHeight;
state.isMoving = true;
state.moveStart = Date.now();
} else if (state.isMoving) {
const elapsed = Date.now() - state.moveStart;
const progress = Math.min(1, elapsed / (totalHeight * 1000 / animSpeed));
state.currentY = zoneHeight - progress * (zoneHeight + totalHeight);
if (progress >= 1) {
state.isMoving = false;
state.isPausing = true;
state.pauseStart = Date.now();
}
} else if (state.isPausing) {
if (Date.now() - state.pauseStart >= pauseMs) {
state.isPausing = false;
state.isMoving = true; //
}
} else if (!state.isPausing && state.isMoving) {
state.currentPage = (state.currentPage + 1) % asset.pages.length;
state.currentY = zoneHeight;
state.isMoving = true;
state.moveStart = Date.now();
}
state.currentX = 0;
break;
case 4: //
if (!state.isMoving && !state.isPausing) {
state.currentY = -totalHeight;
state.isMoving = true;
state.moveStart = Date.now();
} else if (state.isMoving) {
const elapsed = Date.now() - state.moveStart;
const progress = Math.min(1, elapsed / (totalHeight * 1000 / animSpeed));
state.currentY = -totalHeight + progress * (zoneHeight + totalHeight);
if (progress >= 1) {
state.isMoving = false;
state.isPausing = true;
state.pauseStart = Date.now();
}
} else if (state.isPausing) {
if (Date.now() - state.pauseStart >= pauseMs) {
state.isPausing = false;
state.isMoving = true; //
}
} else if (!state.isPausing && state.isMoving) {
state.currentPage = (state.currentPage + 1) % asset.pages.length;
state.currentY = -totalHeight;
state.isMoving = true;
state.moveStart = Date.now();
}
state.currentX = 0;
break;
case 5: // ()
// -
if (typeof state.currentX !== 'number') state.currentX = zoneWidth;
state.currentX -= animSpeed;
if (state.currentX < -totalWidth) {
state.currentX += totalWidth;
}
state.currentY = (zoneHeight - totalHeight) / 2;
break;
case 6: //
if (!state.isPausing && !state.isMoving) {
state.isPausing = true;
state.pauseStart = Date.now();
} else if (state.isPausing) {
if (Date.now() - state.pauseStart >= pauseMs) {
state.isPausing = false;
state.isMoving = true; //
}
} else if (state.isMoving) {
state.currentPage = (state.currentPage + 1) % asset.pages.length;
state.isMoving = false;
state.isPausing = true;
state.pauseStart = Date.now();
}
state.currentX = 0;
state.currentY = 0;
break;
default: // (effect === 0)
state.currentX = 0;
state.currentY = 0;
break;
} }
state.currentY = 0; });
break; },
case 2: //
if (typeof state.currentX !== 'number') state.currentX = -totalWidth;
state.currentX += animSpeed;
if (state.currentX > zoneWidth) {
if (asset.pages.length > 1) {
state.currentPage = (state.currentPage + 1) % asset.pages.length;
}
state.currentX = -totalWidth;
}
state.currentY = 0;
break;
case 3: //
if (typeof state.currentY !== 'number') state.currentY = zoneHeight;
state.currentY -= animSpeed;
if (state.currentY < -totalHeight) {
if (asset.pages.length > 1) {
state.currentPage = (state.currentPage + 1) % asset.pages.length;
}
state.currentY = zoneHeight;
}
state.currentX = 0;
break;
case 4: //
if (typeof state.currentY !== 'number') state.currentY = -totalHeight;
state.currentY += animSpeed;
if (state.currentY > zoneHeight) {
if (asset.pages.length > 1) {
state.currentPage = (state.currentPage + 1) % asset.pages.length;
}
state.currentY = -totalHeight;
}
state.currentX = 0;
break;
case 5: // ()
//
if (typeof state.currentX !== 'number') state.currentX = zoneWidth;
//
state.currentX -= animSpeed;
//
if (state.currentX < -totalWidth) {
state.currentX += totalWidth;
}
// Y
state.currentY = (zoneHeight - totalHeight) / 2;
break;
case 6: //
//
state.currentX = 0;
state.currentY = 0;
break;
default: // (effect === 0)
state.currentX = 0;
state.currentY = 0;
break;
}
});
},
measureTextWidth(text, asset) { measureTextWidth(text, asset) {
// //
const canvas = document.createElement('canvas'); const canvas = document.createElement('canvas');