lefengyang
2026-08-17 41f508ebc1471628b0421342109084ab637f5a9d
制作动画功能优化
1 files modified
178 ■■■■ changed files
ledApp/pages/addTemplate/addTemplate.vue 178 ●●●● patch | view | raw | blame | history
ledApp/pages/addTemplate/addTemplate.vue
@@ -129,7 +129,7 @@
                </view>
                <!-- 操作按钮 -->
                <view class="controls" style="left: 30rpx;">
                    <uv-button type="primary" :plain="true" :text="showControls ? '收回' : '操作'" @click="toggleControls"></uv-button>
                    <uv-button type="success" :plain="true" :text="showControls ? '收回' : '操作'" @click="toggleControls"></uv-button>
                </view>
                <!-- 动画按钮 -->
@@ -143,17 +143,26 @@
                
                <!-- 文字生成图形 -->
                <canvas canvas-id="myCanvas" id="myCanvas" class="canvas" :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"></canvas>
                <!-- canvasWidth:{{ canvasWidth }} | canvasHeight:{{ canvasHeight }} -->
                <view class="pixel_grid" @touchstart.prevent="handleTouchStart" @touchmove.prevent="handleTouchMove"
                    @touchend.prevent="handleTouchEnd" @touchcancel.prevent="handleTouchEnd"
                    :style="{
                        gridTemplateRows: `repeat(${height}, 1fr)`,
                        gridTemplateColumns: `repeat(${width}, 1fr)`
                    }">
                    <view class="pixel_cell" v-for="(cell, cellIndex) in pixelData" :key="cellIndex">
                        <view class="pixel_cell_item" :style="{ backgroundColor: cell }"></view>
                <!-- 点阵图 -->
                <scroll-view scroll-x @scroll="handleScroll">
                    <view
                        class="pixel_grid"
                        @touchstart.prevent="handleTouchStart"
                        @touchmove.prevent="handleTouchMove"
                        @touchend.prevent="handleTouchEnd"
                        @touchcancel.prevent="handleTouchEnd"
                        :style="{
                            gridTemplateRows: `repeat(${height}, 1fr)`,
                            gridTemplateColumns: `repeat(${width * (modeVal == 2 ? 10 : 1)}, 1fr)`,
                            width: modeVal == 2 ? '1000vw': '100vw',
                        }">
                        <view class="pixel_cell" v-for="(cell, cellIndex) in pixelData" :key="cellIndex">
                            <view class="pixel_cell_item" :style="{ backgroundColor: cell }"></view>
                        </view>
                    </view>
                </view>
                </scroll-view>
            </view>
        </uv-popup>
        
@@ -199,6 +208,10 @@
                    {
                        name: '动画帧切换',
                        val: 1
                    },
                    {
                        name: '滚动动画',
                        val: 2
                    }
                ],
                modeVal: 0,
@@ -261,8 +274,8 @@
                previewTimer: null,
                previewWidth: 24,
                previewHeight: 12,
                showControls: false
                showControls: false,
                scrollLeft: 0,
            }
        },
        computed: {
@@ -300,6 +313,9 @@
            }
        },
        methods: {
            handleScroll(e){
                this.scrollLeft = e.detail.scrollLeft
            },
            // 获取编辑详情
            getTempleteInfo(){
                templeteInfo(this.tId).then(res=>{
@@ -412,6 +428,15 @@
                        return this.mergeRowsToString(item)
                    });
                    this.fpsTotal = this.total
                } else if (this.modeVal == 2) {
                    const scrollFrames = this.generateScrollFrames(this.fpsData[0]);
                    const parsedData = scrollFrames.map(item => {
                        return this.toSnakeData(item, this.specWidth);
                    });
                    this.animationData = parsedData.map(item => {
                        return this.mergeRowsToString(item);
                    });
                    this.fpsTotal = scrollFrames.length;
                }
                console.log('animationData--------', this.animationData)
                
@@ -511,10 +536,10 @@
                console.log('ratio---', ratio[0]);
                this.height = Number(ratio[0]);
                this.width = Number(ratio[1]);
                this.canvasWidth = this.width * 10;
                this.canvasWidth = this.width * (this.modeVal == 2 ? 10 * 10 : 10);
                this.canvasHeight = this.height * 10;
                // 重新初始化一维数组
                this.pixelData = new Array(this.height * this.width).fill('')
                const colCount = this.width * (this.modeVal == 2 ? 10 : 1)
                this.pixelData = new Array(this.height * colCount).fill('')
                console.log('pixelData---', this.pixelData);
            },
            // 预先获取像素网格的位置信息
@@ -532,14 +557,16 @@
            getCellByTouch(touch, cb) {
                this.getGridRect(rect => {
                    if (!rect) return
                    const x = touch.clientX - rect.left
                    const x = touch.clientX - rect.left + this.scrollLeft
                    const y = touch.clientY - rect.top
                    const cellWidth = rect.width / this.width
                    const colCount = this.width * (this.modeVal == 2 ? 10 : 1)
                    const cellWidth = rect.width / colCount
                    const cellHeight = rect.height / this.height
                    const colIndex = Math.floor(x / cellWidth)
                    const rowIndex = Math.floor(y / cellHeight)
                    console.log('colCount', colCount, 'colIndex', colIndex)
                    if (
                        colIndex >= 0 && colIndex < this.width &&
                        colIndex >= 0 && colIndex < colCount &&
                        rowIndex >= 0 && rowIndex < this.height
                    ) {
                        cb({
@@ -579,7 +606,8 @@
            handleClick(rowIndex, colIndex) {
                console.log(rowIndex, colIndex)
                // 计算一维数组的索引
                const index = rowIndex * this.width + colIndex
                const colCount = this.width * (this.modeVal == 2 ? 10 : 1)
                const index = rowIndex * colCount + colIndex
                // 如果当前像素有颜色,则清除;如果没有颜色,则添加当前选择的颜色
                const currentColor = this.pixelData[index]
                const newColor = currentColor ? '' : this.color
@@ -587,8 +615,9 @@
            },
            clear() {
                ctx && ctx.clearRect(0, 0, 1000, 1000)
                const colCount = this.width * (this.modeVal == 2 ? 10 : 1)
                // 将所有像素点设置为空字符串
                this.pixelData = new Array(this.height * this.width).fill('')
                this.pixelData = new Array(this.height * colCount).fill('')
            },
            toggleControls() {
                this.showControls = !this.showControls;
@@ -612,10 +641,15 @@
            popupAnimationOpen(index){
                this.$refs.popupAnimation.open();
                this.fpsIndex = index
                this.canvasWidth = this.width * (this.modeVal == 2 ? 10 * 10 : 10);
                
                if(this.fpsList[this.fpsIndex] == null){
                    console.log('第一次编辑-----null')
                    this.pixelData = new Array(this.height * this.width).fill('')
                    let pointCount = this.height * this.width
                    if (this.modeVal == 2) {
                        pointCount *= 10
                    }
                    this.pixelData = new Array(pointCount).fill('')
                }else{
                    console.log('已编辑过-----')
                    this.pixelData = this.fpsList[this.fpsIndex]
@@ -624,7 +658,8 @@
            // 关闭模版动画
            popupAnimationClose(){
                this.$refs.popupAnimation.close();
                this.pixelData = new Array(this.height * this.width).fill('')
                const colCount = this.width * (this.modeVal == 2 ? 10 : 1)
                this.pixelData = new Array(this.height * colCount).fill('')
            },
            // 屏幕规格
            changeSpec(e) {
@@ -649,6 +684,51 @@
                const rowStrings = rows.map(r => r.join('')) // 每行合并
                return rowStrings.join('') // 所有行合并
            },
            // 生成滚动动画帧数据(modeVal==2)
            // 从点阵图左侧开始,按照设定分辨率窗口提取动画帧,每帧左移3个像素,剩余全空时停止
            generateScrollFrames(data) {
                const cols = this.specWidth;
                const rows = this.specHeight;
                const totalCols = this.specWidth * 10;
                const step = 3;
                let grid = [];
                for (let r = 0; r < rows; r++) {
                    grid.push(data.slice(r * totalCols, (r + 1) * totalCols));
                }
                const frames = [];
                let offset = 0;
                while (offset < totalCols) {
                    let remainingEmpty = true;
                    for (let r = 0; r < rows; r++) {
                        for (let c = offset; c < totalCols; c++) {
                            if (grid[r][c] !== '' && grid[r][c] !== '000000') {
                                remainingEmpty = false;
                                break;
                            }
                        }
                        if (!remainingEmpty) break;
                    }
                    if (remainingEmpty) break;
                    let frame = [];
                    for (let r = 0; r < rows; r++) {
                        let row = grid[r].slice(offset, offset + cols);
                        while (row.length < cols) {
                            row.push('000000');
                        }
                        frame = frame.concat(row);
                    }
                    frames.push(frame);
                    offset += step;
                }
                return frames;
            },
            // 生成一帧动画其他帧数据
            generateFrames(data, frameCount = 24) {
                const cols = this.specWidth; // 每行 24 个
@@ -693,7 +773,8 @@
            changeMode(e) {
                this.fpsList = []
                this.fpsData = []
                if (e == 0) {
                this.gridRect = null
                if (e == 0 || e == 2) {
                    this.total = 1
                } else {
                    this.total = 2
@@ -775,17 +856,22 @@
                    // 设置字体样式
                    ctx.setFontSize(this.canvasHeight);
                    ctx.setFillStyle('#000')
                    ctx.setTextAlign('center')
                    ctx.setTextBaseline('middle')
                    // 绘制文字
                    // 计算文字位置
                    // 水平居中
                    const x = this.canvasWidth / 2;
                    // 垂直居中,稍微向上偏移一点
                    const y = this.canvasHeight / 2 + 10;
                    // 绘制文字
                    ctx.fillText(char, x, y);
                    if (this.modeVal == 2) {
                        // 滚动动画:文字靠左对齐
                        ctx.setTextAlign('left')
                        const x = 20;
                        ctx.fillText(char, x, y);
                    } else {
                        // 其他模式:文字水平居中
                        ctx.setTextAlign('center')
                        const x = this.canvasWidth / 2;
                        ctx.fillText(char, x, y);
                    }
            
                    // 绘制到画布
                    ctx.draw(false, () => {
@@ -797,18 +883,19 @@
                            width: this.canvasWidth,
                            height: this.canvasHeight,
                            success: (res) => {
                                console.log(res)
                                console.log('canvasGetImageData', res)
                                const imageData = res.data;
                                // 生成一维数组
                                const pixelArray = new Array(this.height * this.width).fill('')
                                const colCount = this.width * (this.modeVal == 2 ? 10 : 1)
                                const pixelArray = new Array(this.height * colCount).fill('')
                                // 计算每个格子的像素大小
                                const cellWidth = this.canvasWidth / this.width;
                                const cellWidth = this.canvasWidth / colCount;
                                const cellHeight = this.canvasHeight / this.height;
            
                                // 生成一维数组
                                for (let y = 0; y < this.height; y++) {
                                    for (let x = 0; x < this.width; x++) {
                                    for (let x = 0; x < colCount; x++) {
                                        let blackPixels = 0;
                                        let totalPixels = 0;
            
@@ -826,7 +913,7 @@
            
                                                // 计算亮度
                                                const brightness = 0.3 * r + 0.59 * g + 0.11 * b;
                                                // 如果亮度小于阈值且透明度大于阈值,则认为是黑色
                                                if (brightness < 128 && a > 128) {
                                                    blackPixels++;
@@ -834,11 +921,11 @@
                                                totalPixels++;
                                            }
                                        }
                                        // 如果黑色像素占比超过阈值,则设置为当前选择的颜色
                                        const isBlack = blackPixels / totalPixels > 0.3;
                                        const arrayIndex = y * this.width + x;
                                        pixelArray[arrayIndex] = isBlack ? this.color : ''; // 改为颜色值或空字符串
                                        const arrayIndex = y * colCount + x;
                                        pixelArray[arrayIndex] = isBlack ? this.color : '';
                                    }
                                }
            
@@ -860,7 +947,7 @@
                    });
                    return;
                }
                this.previewWidth = this.width;
                this.previewWidth = this.modeVal == 2 ? this.specWidth : this.width;
                this.previewHeight = this.height;
                this.previewFrames = this.buildPreviewFrames();
                this.previewFrameIndex = 0;
@@ -878,6 +965,10 @@
                    const data = this.fpsList[0];
                    if (!data) return [];
                    return this.generatePreviewFrames(data, 24);
                } else if (this.modeVal == 2) {
                    const data = this.fpsList[0];
                    if (!data) return [];
                    return this.generateScrollFrames(data);
                } else {
                    const frames = [];
                    for (let i = 0; i < this.total; i++) {
@@ -1028,11 +1119,8 @@
            top: -9999px;
        }
        .pixel_grid {
            position: fixed;
            left: 0;
            top: var(--status-bar-height);
            right: 0;
            bottom: 0;
            height: 85vh;
            border-bottom: 1px solid #eee;
            display: grid;
            gap: 1px;
            box-sizing: border-box;