lefengyang
2026-08-16 c6ba8b8863ffc5ef0d9dc0bd392966a7700c7812
增加预览功能
2 files modified
156 ■■■■■ changed files
ledApp/pages/addTemplate/addTemplate.vue 155 ●●●●● patch | view | raw | blame | history
ledApp/pages/template/template.vue 1 ●●●● patch | view | raw | blame | history
ledApp/pages/addTemplate/addTemplate.vue
@@ -97,7 +97,22 @@
        <view class="footer_none"></view>
        <view class="footer_btn">
            <uv-button text="保存" color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)" shape="circle" @click="clickTempleteAdd()"></uv-button>
            <uv-button text="预览" color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)" shape="circle" customStyle="width:40vw;" @click="openPreview"></uv-button>
            <uv-button text="保存" color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)" shape="circle" customStyle="width:40vw;" @click="clickTempleteAdd()"></uv-button>
        </view>
        <!-- 预览浮层 -->
        <view class="preview_overlay" v-if="previewShow" @click.self="closePreview">
            <view class="preview_title">预览动画</view>
            <view class="preview_matrix">
                <view class="preview_grid" :style="{
                    gridTemplateRows: 'repeat(' + previewHeight + ', auto)',
                    gridTemplateColumns: 'repeat(' + previewWidth + ', auto)'
                }">
                    <view class="preview_cell" v-for="(cell, idx) in currentPreviewFrame" :key="idx" :style="{ backgroundColor: cell || '#1a1a1a' }"></view>
                </view>
            </view>
            <view class="preview_close_btn" @click="closePreview">关闭</view>
        </view>
        <!-- 动画帧数 -->
@@ -234,10 +249,23 @@
                fpsList: [],
                tId: '',
                tempInfo:{},
                titleName: '新增模版'
                titleName: '新增模版',
                previewShow: false,
                previewFrames: [],
                previewFrameIndex: 0,
                previewTimer: null,
                previewWidth: 24,
                previewHeight: 12
                
            }
        },
        computed: {
            currentPreviewFrame() {
                if (this.previewFrames.length === 0) return [];
                const frame = this.previewFrames[this.previewFrameIndex % this.previewFrames.length];
                return frame || [];
            }
        },
        onLoad(opt) {
            this.baseURL = uni.$uv.http.config.baseURL
            if(opt.id){
@@ -254,11 +282,13 @@
        },
        onHide() {
            this.$refs.popupAnimation.close();
            this.stopPreviewAnimation();
            if (typeof plus !== 'undefined') {
                plus.screen.lockOrientation('portrait')
            }
        },
        onUnload() {
            this.stopPreviewAnimation();
            if (typeof plus !== 'undefined') {
                plus.screen.lockOrientation('portrait')
            }
@@ -813,6 +843,77 @@
                });
            },
            
            openPreview() {
                if (this.fpsList.length === 0 || this.fpsList.every(f => f === null || f === undefined)) {
                    uni.showToast({
                        title: '请先编辑动画帧',
                        icon: 'none'
                    });
                    return;
                }
                this.previewWidth = this.width;
                this.previewHeight = this.height;
                this.previewFrames = this.buildPreviewFrames();
                this.previewFrameIndex = 0;
                this.previewShow = true;
                this.$nextTick(() => {
                    this.startPreviewAnimation();
                });
            },
            closePreview() {
                this.stopPreviewAnimation();
                this.previewShow = false;
            },
            buildPreviewFrames() {
                if (this.modeVal == 0) {
                    const data = this.fpsList[0];
                    if (!data) return [];
                    return this.generatePreviewFrames(data, 24);
                } else {
                    const frames = [];
                    for (let i = 0; i < this.total; i++) {
                        if (this.fpsList[i] && this.fpsList[i] !== null) {
                            frames.push(this.fpsList[i]);
                        } else {
                            frames.push(new Array(this.width * this.height).fill(''));
                        }
                    }
                    return frames;
                }
            },
            generatePreviewFrames(data, frameCount) {
                const cols = this.width;
                const rows = this.height;
                const frames = [];
                let grid = [];
                for (let r = 0; r < rows; r++) {
                    grid.push(data.slice(r * cols, (r + 1) * cols));
                }
                for (let f = 0; f < frameCount; f++) {
                    let frame = [];
                    for (let r = 0; r < rows; r++) {
                        let row = grid[r].slice();
                        let shifted = row.slice(f % cols).concat(row.slice(0, f % cols));
                        frame = frame.concat(shifted);
                    }
                    frames.push(frame);
                }
                return frames;
            },
            startPreviewAnimation() {
                this.stopPreviewAnimation();
                if (!this.speed || this.speed <= 0 || this.previewFrames.length === 0) return;
                const interval = 1000 / this.speed;
                this.previewTimer = setInterval(() => {
                    this.previewFrameIndex = (this.previewFrameIndex + 1) % this.previewFrames.length;
                }, interval);
            },
            stopPreviewAnimation() {
                if (this.previewTimer) {
                    clearInterval(this.previewTimer);
                    this.previewTimer = null;
                }
            },
            // 修改添加文字方法
            async addText() {
                uni.showModal({
@@ -950,18 +1051,58 @@
        bottom: 0;
        background: #fff;
        padding: 30rpx 40rpx;
        display: flex;
        justify-content: space-between;
    }
    .footer_btn2 {
    .preview_overlay {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background: #fff;
        padding: 30rpx 40rpx;
        background: rgba(0, 0, 0, 0.85);
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: space-between;
        box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1);
        justify-content: center;
        z-index: 9999;
    }
    .preview_title {
        color: #fff;
        font-size: 34rpx;
        margin-bottom: 40rpx;
    }
    .preview_matrix {
        background: #000;
        padding: 20rpx;
        border-radius: 16rpx;
        box-shadow: 0 0 30rpx rgba(0, 150, 255, 0.3);
    }
    .preview_grid {
        display: grid;
        gap: 2rpx;
        background: #111;
        padding: 4rpx;
        border-radius: 8rpx;
    }
    .preview_cell {
        width: 22rpx;
        height: 22rpx;
        border-radius: 50%;
        background: #1a1a1a;
        box-shadow: inset 0 0 4rpx rgba(0, 0, 0, 0.5);
    }
    .preview_close_btn {
        margin-top: 60rpx;
        width: 300rpx;
        height: 80rpx;
        line-height: 80rpx;
        text-align: center;
        color: #fff;
        background: linear-gradient(142deg, #6FD2FF 0%, #268DFF 100%);
        border-radius: 40rpx;
        font-size: 30rpx;
    }
    .temp_btn {
ledApp/pages/template/template.vue
@@ -33,6 +33,7 @@
        </view>
        <uv-empty v-if="listData.length == 0" mode="list" marginTop="300rpx" iconColor="#eee" textColor="#ddd"></uv-empty>
        <view style="height: 100rpx;"></view>
        
    </view>
</template>