From f04d2ac5332905b12820353a4d5fa42509e041a0 Mon Sep 17 00:00:00 2001
From: lefengyang <lefengyang@tencent.com>
Date: Fri, 14 Aug 2026 23:56:01 +0800
Subject: [PATCH] 去掉无用文件
---
/dev/null | 624 --------------------------------------------------------
ledApp/pages.json | 7
ledApp/pages/addTemplate/addTemplate.vue | 7
3 files changed, 0 insertions(+), 638 deletions(-)
diff --git a/ledApp/pages.json b/ledApp/pages.json
index 1be60ba..b04e9a6 100644
--- a/ledApp/pages.json
+++ b/ledApp/pages.json
@@ -66,13 +66,6 @@
}
},
{
- "path": "pages/animation/animation",
- "style": {
- "navigationBarTitleText": "编辑动画",
- "orientation": "landscape"
- }
- },
- {
"path": "pages/register/register",
"style": {
"navigationBarTitleText": ""
diff --git a/ledApp/pages/addDevice/addDevice0.vue b/ledApp/pages/addDevice/addDevice0.vue
deleted file mode 100644
index 65f8b60..0000000
--- a/ledApp/pages/addDevice/addDevice0.vue
+++ /dev/null
@@ -1,487 +0,0 @@
-<template>
- <view>
- <uv-navbar title="添加设备" :autoBack="true" :placeholder="true" leftIconColor="#fff" bgColor="#3B74EE"
- titleStyle="color:#fff"></uv-navbar>
-
- <view class="page_bg">
- <view class="searching_ico" @click="getBluetoothDevices">
- <image src="/static/img/ico_1.png" mode="widthFix"></image>
- </view>
-
- <view class="searching_text">
- <view class="name">
- 搜索中
- </view>
- <view class="text">
- 持续自动搜索中...
- </view>
- </view>
- </view>
-
- <view class="bluetooth" v-if="bluetoothList.length != 0">
- <view class="bluetooth_title">
- <image class="ico" src="/static/img/ico_2.png" mode="widthFix"></image>
- <text>已找到设备</text>
- </view>
- <view class="bluetooth_list">
- <view class="item" v-for="(item,index) in bluetoothList" :key="index">
- <image class="ph" src="/static/img/ph_1.png" mode="aspectFill"></image>
- <view class="info">
- <view class="name">
- {{item.device.name}}
- </view>
- <view class="btn">
- <uv-button type="primary" shape="circle" text="连接" size="small"
- @click="createBLEConnection(item.device.address)"></uv-button>
-
- </view>
-
- </view>
- </view>
- </view>
- </view>
-
- </view>
-</template>
-
-<script>
- import {
- BleLib
- } from '@/uni_modules/android-ble'
- var lib = new BleLib()
-
- export default {
- data() {
- return {
- searchIng: true,
- bluetoothList: [],
- deviceId: '', //设备id
- serviceId: '', //服务uuid
- characteristicId: '', //特征uuid
- characteristicId1: '',
- characteristicId2: '',
- characteristicId3: '',
- bluetooth: false
- }
- },
- onLoad() {
-
- },
- onShow() {
- this.bluetooth = lib.isEnabled();
- if (this.bluetooth) {
- console.log('蓝牙开启', this.bluetooth)
- this.getBluetoothDevices()
- } else {
- console.log('蓝牙关闭', this.bluetooth)
- }
- },
- onUnload() {
- //lib.close();
- },
- methods: {
- // 蓝牙扫描
- getBluetoothDevices() {
- console.log('蓝牙扫描----')
- const that = this
- var isconnect = lib.isConnected();
- console.log('蓝牙是否连接----',isconnect)
- if(isconnect){
- console.log('蓝牙是否连接----',isconnect)
- var device = lib.getConnectMac();
- console.log('device----',device)
- }
- lib.startScanBleDevice(15000, function(res) {
- if (res.data.device.name === 'CH9140BLE2U') {
- that.bluetoothList.push(res.data);
- console.log(res.data)
- }
- })
- },
- // 连接蓝牙
- createBLEConnection(address) {
- console.log('address-----',address)
- const that = this
- const isconnect = lib.isConnected();
- console.log('蓝牙是否连接----',isconnect)
- // if(isconnect){
- // const device = lib.getConnectMac();
- // console.log('获取已连接设备id----',device)
- // that.commandCHEC()
- // }
- // else{
- lib.connect(address, false, function(res) {
- console.log('连接', res)
- // type==0 表示连接成功
- if (res.type == 0) {
- // 扫描蓝牙与特征值
- lib.scanServices(function(resSevice) {
- console.log('特征值--------', resSevice)
- that.characteristicId1 = resSevice.data[2].characteristics[0].uuid;
- that.characteristicId2 = resSevice.data[2].characteristics[1].uuid;
- that.characteristicId3 = resSevice.data[2].characteristics[2].uuid;
- that.onNotityReadBleData()
- setTimeout(() => {
- console.log("延迟执行这段代码");
- that.setBaud()
- //that.commandCHEC()
- }, 2000);
- });
- // lib.setMtu(512, function(resMtu) {
- // console.log('设置mtu(仅安卓)', resMtu)
- // })
-
-
- } else {
- if(res.message == 'bluetooth is connected'){
- //that.commandCHEC()
- }
- else{
- uni.showToast({
- title: '蓝牙连接失败',
- duration: 2000,
- icon: 'none'
- });
- }
- }
- })
- // }
- },
- // 开启服务消息通知
- onNotityReadBleData() {
- const that = this
- console.log(that.characteristicId1)
- lib.onNotityReadBleData(lib.getSericUUID(),
- that.characteristicId1,
- true,
- function(res) {
- console.log('开启服务消息通知', res.data)
-
- uni.$uv.debounce(that.extractEncryptedIDWithCheck(res.data), 2000)
- }
- )
- },
- // 构建指令数据
- commandCHEC() {
- //555555555555555555557e41434845430095977e
- // 构建指令数据
- const startBytes = new Array(10).fill(0x55); // 10个启动字节
- const frameHeader = 0x7E; // 帧头
- const packageType = 0x41; // 包类型
- const command = [0x43, 0x48, 0x45, 0x43]; // "CHEC"的ASCII码
- const reserved = 0x00; // 保留字节
-
- // 组合数据
- const data = [
- ...startBytes,
- frameHeader,
- packageType,
- ...command,
- reserved
- ];
-
- // 计算CRC校验码(从包头段开始计算)
- const crc = this.calcCRC16(data.slice(11));
-
- // 添加CRC和帧尾
- data.push((crc >> 8) & 0xFF); // CRC高8位
- data.push(crc & 0xFF); // CRC低8位
- data.push(frameHeader); // 帧尾
-
- setTimeout(() => {
- this.writeDataToBle(data)
- }, 2000);
- },
- // 设置波特率
- setBaud() {
- var that = this
- var d = "06000900009600000801009F"
- lib.writeStringDataToBle(
- lib.getSericUUID(),
- that.characteristicId2,
- d,
- function(res) {
- console.log('设置波特率', res)
- that.commandCHEC()
- }
- )
- },
-
- // 发送写入指令
- writeDataToBle(b) {
- var that = this
- lib.writeDataToBle(
- lib.getSericUUID(),
- that.characteristicId2,
- b,
- function(res) {
- console.log('发送16进制数组', res)
-
- }
- )
- },
- writeStringDataToBle() {
- var that = this
- var d = "555555555555555555557e41434845430095977e"
- lib.writeStringDataToBle(
- lib.getSericUUID(),
- that.characteristicId2,
- d,
- function(res) {
- console.log('发送16进制字符数据', res)
- }
- )
- },
- // 16进制数据转二位数据
- hexStringToArrayBuffer(hexStr) {
- const bytes = new Uint8Array(hexStr.match(/.{2}/g).map(byte => parseInt(byte, 16)));
- return bytes.buffer;
- },
- // 二位数据转16进制数据
- ab2hex(buffer) {
- const hexArr = Array.prototype.map.call(
- new Uint8Array(buffer),
- function(bit) {
- return ('00' + bit.toString(16)).slice(-2)
- }
- )
- return hexArr.join('')
- },
- calcCRC16(data) {
- // 创建查表
- const table = this.buildCRC16Table();
- let crc = 0;
-
- // 计算CRC
- for (let i = 0; i < data.length; i++) {
- crc = ((crc << 8) & 0xFF00) ^ table[((crc >> 8) ^ data[i]) & 0xFF];
- }
-
- return crc & 0xFFFF;
- },
- buildCRC16Table() {
- const table = new Array(256);
- const poly = 0x1021; // CRC-CCITT多项式
-
- for (let i = 0; i < 256; i++) {
- let nData = i << 8;
- let nAccum = 0;
-
- for (let j = 0; j < 8; j++) {
- if ((nData ^ nAccum) & 0x8000) {
- nAccum = (nAccum << 1) ^ poly;
- } else {
- nAccum <<= 1;
- }
- nData <<= 1;
- }
- table[i] = nAccum & 0xFFFF;
- }
- return table;
- },
- // 判断返回数据是否正确 并提取加密id
- extractEncryptedIDWithCheck(buffer) {
- const hexArr = Array.prototype.map.call(
- new Uint8Array(buffer),
- function(bit) {
- return ('00' + bit.toString(16)).slice(-2)
- }
- )
- const responseHex = hexArr.join('').slice(20)
- console.log('responseHex---',responseHex)
- // 1. 提取返回标志位置(第11字节,即下标:30~31)
- const returnFlagHex = responseHex.substring(12, 14).toUpperCase();
- console.log('returnFlagHex---',returnFlagHex)
- if (returnFlagHex === "30") {
- // 正常,提取加密ID(从下标 32 开始,取 40 位
- const encryptedID = responseHex.substring(14, 55).toUpperCase();
- console.log('encryptedID---',encryptedID)
- uni.showToast({
- title: encryptedID,
- duration: 2000,
- icon: 'none'
- });
- } else if (returnFlagHex === "31") {
- // 异常,无加密ID
- uni.showToast({
- title: '智能壳工作异常,返回标志0x31',
- duration: 2000,
- icon: 'none'
- });
- lib.close();
- } else {
- // 未知标志
- uni.showToast({
- title: '智能壳工作异常,未知返回标志',
- duration: 2000,
- icon: 'none'
- });
- lib.close();
- }
- },
- // 解密函数
- decryptSmartShellID(encryptedHex) {
- if (encryptedHex.length !== 40) {
- console.error('加密ID长度必须为40个十六进制字符(20字节)');
- return null;
- }
-
- const key = [
- 0x14, 0x15, 0x0A, 0x0C, 0x24, 0x01, 0x0B, 0x13, 0x44, 0x02,
- 0x0F, 0x00, 0x0E, 0x0D, 0x47, 0x2A, 0x5E, 0x32, 0x32, 0x11
- ];
-
- const decrypted = [];
-
- for (let i = 0; i < 20; i++) {
- const byteHex = encryptedHex.substr(i * 2, 2);
- const encryptedByte = parseInt(byteHex, 16);
- const decryptedByte = (encryptedByte - key[i] + 256) % 256;
- decrypted.push(decryptedByte.toString(16).padStart(2, '0').toUpperCase());
- }
-
- return decrypted.join(' ');
- },
- // 加密函数
- encryptSmartShellID(originalHex) {
- if (originalHex.length !== 40) {
- console.error('原始ID长度必须为40位十六进制字符串(20字节)');
- return null;
- }
-
- // 加密密钥(20 字节)
- const key = [
- 0x14, 0x15, 0x0A, 0x0C, 0x24, 0x01, 0x0B, 0x13, 0x44, 0x02,
- 0x0F, 0x00, 0x0E, 0x0D, 0x47, 0x2A, 0x5E, 0x32, 0x32, 0x11
- ];
-
- const encrypted = [];
-
- for (let i = 0; i < 20; i++) {
- const byteHex = originalHex.substr(i * 2, 2);
- const originalByte = parseInt(byteHex, 16);
- const encryptedByte = (originalByte + key[i]) % 256; // 忽略进位
- encrypted.push(encryptedByte.toString(16).padStart(2, '0').toUpperCase());
- }
-
- return encrypted.join('');
- }
-
-
-
- }
- }
-</script>
-
-<style lang="scss" scoped>
- .bluetooth {
- padding: 0 30rpx;
-
- .bluetooth_list {
- .item {
- background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
- box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
- border-radius: 20rpx;
- border: 2rpx solid #FFFFFF;
- padding: 22rpx;
- display: flex;
- align-items: stretch;
- justify-content: space-between;
- margin-top: 24rpx;
-
- .ph {
- display: block;
- flex-shrink: 0;
- width: 230rpx;
- height: 170rpx;
- border-radius: 20rpx;
- margin-right: 24rpx;
- }
-
- .info {
- width: 100%;
- display: flex;
- flex-direction: column;
- justify-content: space-between;
-
- .name {
- font-weight: 500;
- font-size: 27rpx;
- color: #000000;
- line-height: 50rpx;
- }
-
- .btn {
- width: 160rpx;
- margin-top: 10rpx;
- }
- }
- }
- }
-
- .bluetooth_title {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin-top: 20rpx;
-
- text {
- display: block;
- width: 100%;
- font-weight: 500;
- font-size: 34rpx;
- color: rgba(0, 0, 0, 0.7);
- line-height: 38rpx;
- }
-
- .ico {
- flex-shrink: 0;
- width: 40rpx;
- margin-right: 10rpx;
- }
- }
- }
-
- .searching_text {
- text-align: center;
- background: url('/static/img/bg_3.png') no-repeat center top;
- background-size: 100% auto;
- box-sizing: border-box;
- padding-top: 140rpx;
- margin-top: -100rpx;
- height: 300rpx;
-
- .name {
- font-weight: 500;
- font-size: 36rpx;
- color: #000000;
- line-height: 34rpx;
- }
-
- .text {
- font-size: 26rpx;
- color: rgba(0, 0, 0, 0.5);
- line-height: 34rpx;
- margin-top: 30rpx;
- }
- }
-
- .searching_ico {
- display: flex;
- align-items: center;
- justify-content: center;
- padding-top: 100rpx;
- }
-
- .page_bg {
- width: 100%;
- background: linear-gradient(3deg, #72E5FF 0%, #3B74EE 98%, #3B74EE 100%);
- }
-
- .page_bg1 {
- width: 100%;
- min-height: calc(100vh - var(--window-bottom));
- background: url('/static/img/bg_2.jpg') no-repeat center top;
- background-size: 100% auto;
- }
-</style>
\ No newline at end of file
diff --git a/ledApp/pages/addTemplate/addTemplate copy 1.vue b/ledApp/pages/addTemplate/addTemplate copy 1.vue
deleted file mode 100644
index d90c1a9..0000000
--- a/ledApp/pages/addTemplate/addTemplate copy 1.vue
+++ /dev/null
@@ -1,548 +0,0 @@
-<template>
- <view class="page_bg">
- <uv-navbar title="新增模版" :autoBack="true" :placeholder="true" leftIconColor="#fff" bgColor="rgba(0,0,0,0)"
- titleStyle="color:#fff"></uv-navbar>
-
- <view class="temp">
- <view class="temp_item">
- <view class="title">
- 模版名称
- </view>
- <view class="item">
- <uv-input placeholder="请输入名称" border="none" v-model="tempName" fontSize="27rpx"
- customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
- </view>
- </view>
- <view class="temp_item">
- <view class="title">
- 封面图片
- </view>
- <view class="item item_pic">
- <uv-upload :fileList="fileList1" name="1" :maxCount="1" width="200rpx" height="200rpx"
- :previewFullImage="false" @afterRead="afterRead" @delete="deletePic">
- </uv-upload>
- </view>
- </view>
- <view class="temp_item">
- <view class="title">
- 屏幕规格
- </view>
- <view class="item">
- <uv-radio-group v-model="specVal" placement="column" @change="changeSpec">
- <uv-radio class="spec_radio" v-for="(item, index) in specList" :key="index" :label="item.name"
- :name="item.name">
- </uv-radio>
- </uv-radio-group>
- </view>
- </view>
- <view class="temp_item">
- <view class="title">
- 动画方式
- </view>
- <view class="item">
- <uv-radio-group v-model="modeVal" placement="column" @change="changeMode">
- <uv-radio class="spec_radio" v-for="(item, index) in modeList" :key="index" :label="item.name"
- :name="item.val">
- </uv-radio>
- </uv-radio-group>
- </view>
- </view>
-
- <view class="temp_item">
- <view class="title">
- 动画帧数
- </view>
- <view class="item" @click="clickTotal">
- <uv-input placeholder="请输入" readonly border="none" v-model="total" suffixIcon="arrow-right"
- suffixIconStyle="color: #bbb" fontSize="26rpx"
- customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
- </view>
- </view>
- <view class="temp_item">
- <view class="title">
- 动画速度
- </view>
- <view class="item" @click="clickSpeed">
- <uv-input placeholder="请输入" readonly border="none" v-model="speed" suffixIcon="arrow-right"
- suffixIconStyle="color: #bbb" fontSize="26rpx"
- customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
- </view>
- </view>
- <!-- <view class="temp_item" v-if="modeVal == 1">
- <view class="title">
- 循环次数
- </view>
- <view class="item">
- <uv-input placeholder="请输入" readonly border="none" v-model="cycle" suffixIcon="arrow-right"
- suffixIconStyle="color: #bbb" fontSize="26rpx" customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
- </view>
- </view> -->
-
- <view class="temp_item1" v-for="(item,index) in total" :key="index">
- <view class="title">
- 动画第{{item}}帧
- </view>
- <view class="item">
- <uv-button text="编辑" @click="clickGoAnim(index)" shape="circle" size="small"
- color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)"></uv-button>
- </view>
- </view>
-
- </view>
-
-
- <!-- <view class="temp_btn" @click="clickGoAnim">
- 编辑动画
- </view> -->
-
- <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"
- style="margin-top: 30rpx;" @click="clickTempleteAdd2()"></uv-button>
- </view>
-
- <!-- 动画帧数 -->
- <uv-picker ref="pickerTotal" :columns="columnsTotal" @confirm="confirmTotal"></uv-picker>
-
- <!-- 动画速度 -->
- <uv-picker ref="pickerSpeed" :columns="columnsSpeed" @confirm="confirmSpeed"></uv-picker>
-
-
- </view>
-</template>
-
-<script>
- import {
- templeteAdd
- } from '@/api/api.js'
- export default {
- data() {
- return {
- tempName: '',
- pic: '',
- specList: [{
- name: '12*24'
- }, {
- name: '16*24'
- }, {
- name: '18*24'
- }],
- specVal: '12*24',
- specWidth: 24,
- specHeight: 12,
- modeList: [{
- name: '单个动画帧',
- val: 0
- },
- {
- name: '动画帧切换',
- val: 1
- }
- ],
- modeVal: 0,
- columnsTotal: [
- [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
- 28, 29, 30
- ]
- ],
- total: 1, //动画总帧数
- columnsSpeed: [
- [10, 20, 30, 40, 50, 60, 70, 80, 90]
- ],
- speed: 10,
- cycle: 1,
- fpsData: [],
- fpsIndex: 1, //第几帧
- fpsIds: [],
-
- baseURL: '',
- fileList1: [],
- animationData: [],
- fpsTotal: 1
- }
- },
- onLoad() {
- this.baseURL = uni.$uv.http.config.baseURL
- },
- onShow() {
- console.log('------onShow')
- //const fpsParams = uni.getStorageSync('fpsParams');
- // if (fpsParams) {
- // console.log('收到回传参数:', fpsParams);
- // this.fpsData[fpsParams.fpsIndex] = fpsParams.data;
- // uni.removeStorageSync('fpsParams');
- // }
- const that = this
- uni.getStorage({
- key: 'fpsParams',
- success: (res) => {
- console.log('获取成功:', res.data);
- that.fpsData[res.data.fpsIndex] = res.data.data;
- },
- fail: (err) => {
- console.error('获取失败:', err);
- }
- });
- },
- methods: {
- // 屏幕规格
- changeSpec(e) {
- const ratio = e.split('*');
- this.specHeight = Number(ratio[0]);
- this.specWidth = Number(ratio[1]);
- },
- // 偶数行数据反转
- toSnakeData(pixelData, cols = 24) {
- const rows = []
- for (let i = 0; i < pixelData.length; i += cols) {
- let row = pixelData.slice(i, i + cols)
- if ((rows.length + 1) % 2 === 0) {
- row.reverse() // 偶数行反转
- }
- rows.push(row)
- }
- return rows
- },
- // 二维数组转字符串
- mergeRowsToString(rows) {
- const rowStrings = rows.map(r => r.join('')) // 每行合并
- return rowStrings.join('') // 所有行合并
- },
- // 生成一帧动画其他帧数据
- generateFrames(data, frameCount = 24) {
- const cols = this.specWidth; // 每行 24 个
- const rows = this.specHeight; // 共 12 行
- const frames = [];
- // 拆分成 12 行
- 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 = [];
- // 每行左移 f 次
- 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;
- },
- clickTempleteAdd2() {
-
- uni.showLoading({
- title: '添加中',
- mask: true
- });
-
- templeteAdd({
- name: '',
- userId: uni.getStorageSync('userId'),
- screen: '', //屏幕
- fpsTotal: '', //总帧数
- speed: '', //速度
- pic: '',
- fpsData: ''
- }).then(res => {
- console.log('添加动画模版', res)
- uni.hideLoading();
- uni.navigateBack({
- delta: 1
- });
- })
-
- },
- clickTempleteAdd() {
- if (!this.tempName) {
- uni.showToast({
- title: '请输入模版名称',
- duration: 2000,
- icon: 'none'
- });
- return
- }
- if (this.fileList1.length == 0) {
- uni.showToast({
- title: '请上传封面图片',
- duration: 2000,
- icon: 'none'
- });
- return
- }
- if (this.fpsData.length == 0) {
- uni.showToast({
- title: '请编辑动画帧',
- duration: 2000,
- icon: 'none'
- });
- return
- }
- //console.log('fpsData------------', this.fpsData)
- // 循环校验每一帧是否完整
- // let missingIndex = -1;
- // for (let i = 0; i < this.total; i++) {
- // if (this.fpsData[i] === null || this.fpsData[i] === undefined) {
- // missingIndex = i;
- // break;
- // }
- // }
- // if (missingIndex !== -1) {
- // uni.showToast({
- // title: '请编辑第' + (missingIndex + 1) + '帧',
- // duration: 2000,
- // icon: 'none'
- // });
- // return;
- // }
-
- uni.showLoading({
- title: '添加中',
- mask: true
- });
- // 动画方式
- if (this.modeVal == 0) { //一帧动画
- let frames = this.generateFrames(this.fpsData[0], 24);
- const parsedData = frames.map(item => {
- return this.toSnakeData(item, this.specWidth)
- });
- this.animationData = parsedData.map(item => {
- return this.mergeRowsToString(item)
- });
- this.fpsTotal = 24
- } else if (this.modeVal == 1) {
- const parsedData = this.fpsData.map(item => {
- return this.toSnakeData(item, this.specWidth)
- });
- this.animationData = parsedData.map(item => {
- return this.mergeRowsToString(item)
- });
- this.fpsTotal = this.total
- }
-
- // setTimeout(() => {
-
- // }, 1000)
- const addData = {
- name: this.tempName,
- userId: uni.getStorageSync('userId'),
- screen: this.specVal, //屏幕
- fpsTotal: this.fpsTotal, //总帧数
- speed: this.speed, //速度
- pic: this.fileList1[0].url,
- fpsData: JSON.stringify(this.animationData)
- }
- console.log('addData-----', addData)
- templeteAdd(addData).then(res => {
- console.log('添加动画模版', res)
- uni.hideLoading();
- if (res.code == 200) {
- uni.navigateBack({
- delta: 1
- });
- } else {
- uni.showToast({
- title: res.msg,
- duration: 1500,
- icon: 'none',
- mask: true
- });
- }
- })
-
- },
- clickSpeed() {
- this.$refs.pickerSpeed.open();
- },
- confirmSpeed(e) {
- console.log('confirmSpeed', e.value[0]);
- this.speed = e.value[0]
- },
- clickTotal() {
- if (this.total != 1) {
- this.$refs.pickerTotal.open();
- }
- },
- confirmTotal(e) {
- console.log('confirmTotal', e.value[0]);
- this.total = e.value[0]
- // this.fpsData = []
- // for (let i = 0; i < this.total; i++) {
- // this.fpsData.push(null);
- // }
- this.adjustFpsData()
- console.log('fpsData------------', this.fpsData)
- },
- changeMode(e) {
- this.fpsData = []
- if (e == 0) {
- this.total = 1
- } else {
- this.total = 2
- }
- this.adjustFpsData()
- },
- clickGoAnim(index) {
- uni.navigateTo({
- url: '/pages/animation/animation?fps=' + index
- })
- },
- adjustFpsData() {
- // 如果 fpsData 太长 → 截取
- if (this.fpsData.length > this.total) {
- this.fpsData = this.fpsData.slice(0, this.total);
- }
- // 如果 fpsData 太短 → 补齐 null
- else if (this.fpsData.length < this.total) {
- const missing = this.total - this.fpsData.length;
- this.fpsData = this.fpsData.concat(new Array(missing).fill(null));
- }
- },
- // 删除图片
- deletePic(event) {
- this[`fileList${event.name}`].splice(event.index, 1)
- },
- // 新增图片
- async afterRead(event) {
- console.log('event---', event)
- // 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
- let lists = [].concat(event.file)
- let fileListLen = this[`fileList${event.name}`].length
- lists.map((item) => {
- this[`fileList${event.name}`].push({
- ...item,
- status: 'uploading',
- message: '上传中'
- })
- })
- for (let i = 0; i < lists.length; i++) {
- const result = await this.uploadFilePromise(lists[i].url)
- this.avatar = result
- let item = this[`fileList${event.name}`][fileListLen]
- this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
- status: 'success',
- message: '',
- url: result
- }))
- fileListLen++
- }
- },
- uploadFilePromise(url) {
- return new Promise((resolve, reject) => {
- let a = uni.uploadFile({
- url: this.baseURL + '/common/uploadFileByAnny', // 仅为示例,非真实的接口地址
- filePath: url,
- name: 'file',
- success: (res) => {
- console.log('图片-----', JSON.parse(res.data).fileName)
- setTimeout(() => {
- resolve(JSON.parse(res.data).fileName)
- }, 1000)
- }
- });
- })
- }
- }
- }
-</script>
-
-<style lang="scss" scoped>
- .footer_none {
- height: 200rpx;
- }
-
- .footer_btn {
- position: fixed;
- left: 0;
- right: 0;
- bottom: 0;
- background: #fff;
- padding: 30rpx 40rpx;
- }
-
- .temp_btn {
- position: fixed;
- left: 30rpx;
- bottom: 60rpx;
- right: 30rpx;
- box-sizing: border-box;
- height: 96rpx;
- background: linear-gradient(142deg, #6FD2FF 0%, #268DFF 100%);
- border-radius: 385rpx 385rpx 385rpx 385rpx;
- font-size: 30rpx;
- color: #FFFFFF;
- line-height: 96rpx;
- text-align: center;
- }
-
- .spec_radio {
- margin-top: 30rpx;
-
- &:first-child {
- margin-top: 10rpx;
- }
- }
-
- .temp {
- padding: 0 30rpx;
-
- .temp_item1 {
- display: flex;
- align-items: center;
- justify-content: space-between;
- background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
- box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
- border-radius: 23rpx 23rpx 23rpx 23rpx;
- border: 2rpx solid #FFFFFF;
- padding: 30rpx;
- box-sizing: border-box;
- margin-top: 24rpx;
-
- .title {
- flex-shrink: 0;
- font-size: 30rpx;
- color: rgba(0, 0, 0, 0.9);
- line-height: 40rpx;
-
- }
-
- .item {}
- }
-
- .temp_item {
- background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
- box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
- border-radius: 23rpx 23rpx 23rpx 23rpx;
- border: 2rpx solid #FFFFFF;
- padding: 30rpx;
- box-sizing: border-box;
- margin-top: 24rpx;
-
- .title {
- flex-shrink: 0;
- font-size: 30rpx;
- color: rgba(0, 0, 0, 0.9);
- line-height: 40rpx;
- }
-
- .item {
- margin-top: 20rpx;
-
- &.item_pic {
- margin-top: 25rpx;
- }
- }
- }
- }
-
- .page_bg {
- width: 100%;
- min-height: calc(100vh - var(--window-bottom));
- background: url('/static/img/bg_2.jpg') no-repeat center top;
- background-size: 100% auto;
- }
-</style>
\ No newline at end of file
diff --git a/ledApp/pages/addTemplate/addTemplate copy 2.vue b/ledApp/pages/addTemplate/addTemplate copy 2.vue
deleted file mode 100644
index d90c1a9..0000000
--- a/ledApp/pages/addTemplate/addTemplate copy 2.vue
+++ /dev/null
@@ -1,548 +0,0 @@
-<template>
- <view class="page_bg">
- <uv-navbar title="新增模版" :autoBack="true" :placeholder="true" leftIconColor="#fff" bgColor="rgba(0,0,0,0)"
- titleStyle="color:#fff"></uv-navbar>
-
- <view class="temp">
- <view class="temp_item">
- <view class="title">
- 模版名称
- </view>
- <view class="item">
- <uv-input placeholder="请输入名称" border="none" v-model="tempName" fontSize="27rpx"
- customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
- </view>
- </view>
- <view class="temp_item">
- <view class="title">
- 封面图片
- </view>
- <view class="item item_pic">
- <uv-upload :fileList="fileList1" name="1" :maxCount="1" width="200rpx" height="200rpx"
- :previewFullImage="false" @afterRead="afterRead" @delete="deletePic">
- </uv-upload>
- </view>
- </view>
- <view class="temp_item">
- <view class="title">
- 屏幕规格
- </view>
- <view class="item">
- <uv-radio-group v-model="specVal" placement="column" @change="changeSpec">
- <uv-radio class="spec_radio" v-for="(item, index) in specList" :key="index" :label="item.name"
- :name="item.name">
- </uv-radio>
- </uv-radio-group>
- </view>
- </view>
- <view class="temp_item">
- <view class="title">
- 动画方式
- </view>
- <view class="item">
- <uv-radio-group v-model="modeVal" placement="column" @change="changeMode">
- <uv-radio class="spec_radio" v-for="(item, index) in modeList" :key="index" :label="item.name"
- :name="item.val">
- </uv-radio>
- </uv-radio-group>
- </view>
- </view>
-
- <view class="temp_item">
- <view class="title">
- 动画帧数
- </view>
- <view class="item" @click="clickTotal">
- <uv-input placeholder="请输入" readonly border="none" v-model="total" suffixIcon="arrow-right"
- suffixIconStyle="color: #bbb" fontSize="26rpx"
- customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
- </view>
- </view>
- <view class="temp_item">
- <view class="title">
- 动画速度
- </view>
- <view class="item" @click="clickSpeed">
- <uv-input placeholder="请输入" readonly border="none" v-model="speed" suffixIcon="arrow-right"
- suffixIconStyle="color: #bbb" fontSize="26rpx"
- customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
- </view>
- </view>
- <!-- <view class="temp_item" v-if="modeVal == 1">
- <view class="title">
- 循环次数
- </view>
- <view class="item">
- <uv-input placeholder="请输入" readonly border="none" v-model="cycle" suffixIcon="arrow-right"
- suffixIconStyle="color: #bbb" fontSize="26rpx" customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
- </view>
- </view> -->
-
- <view class="temp_item1" v-for="(item,index) in total" :key="index">
- <view class="title">
- 动画第{{item}}帧
- </view>
- <view class="item">
- <uv-button text="编辑" @click="clickGoAnim(index)" shape="circle" size="small"
- color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)"></uv-button>
- </view>
- </view>
-
- </view>
-
-
- <!-- <view class="temp_btn" @click="clickGoAnim">
- 编辑动画
- </view> -->
-
- <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"
- style="margin-top: 30rpx;" @click="clickTempleteAdd2()"></uv-button>
- </view>
-
- <!-- 动画帧数 -->
- <uv-picker ref="pickerTotal" :columns="columnsTotal" @confirm="confirmTotal"></uv-picker>
-
- <!-- 动画速度 -->
- <uv-picker ref="pickerSpeed" :columns="columnsSpeed" @confirm="confirmSpeed"></uv-picker>
-
-
- </view>
-</template>
-
-<script>
- import {
- templeteAdd
- } from '@/api/api.js'
- export default {
- data() {
- return {
- tempName: '',
- pic: '',
- specList: [{
- name: '12*24'
- }, {
- name: '16*24'
- }, {
- name: '18*24'
- }],
- specVal: '12*24',
- specWidth: 24,
- specHeight: 12,
- modeList: [{
- name: '单个动画帧',
- val: 0
- },
- {
- name: '动画帧切换',
- val: 1
- }
- ],
- modeVal: 0,
- columnsTotal: [
- [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
- 28, 29, 30
- ]
- ],
- total: 1, //动画总帧数
- columnsSpeed: [
- [10, 20, 30, 40, 50, 60, 70, 80, 90]
- ],
- speed: 10,
- cycle: 1,
- fpsData: [],
- fpsIndex: 1, //第几帧
- fpsIds: [],
-
- baseURL: '',
- fileList1: [],
- animationData: [],
- fpsTotal: 1
- }
- },
- onLoad() {
- this.baseURL = uni.$uv.http.config.baseURL
- },
- onShow() {
- console.log('------onShow')
- //const fpsParams = uni.getStorageSync('fpsParams');
- // if (fpsParams) {
- // console.log('收到回传参数:', fpsParams);
- // this.fpsData[fpsParams.fpsIndex] = fpsParams.data;
- // uni.removeStorageSync('fpsParams');
- // }
- const that = this
- uni.getStorage({
- key: 'fpsParams',
- success: (res) => {
- console.log('获取成功:', res.data);
- that.fpsData[res.data.fpsIndex] = res.data.data;
- },
- fail: (err) => {
- console.error('获取失败:', err);
- }
- });
- },
- methods: {
- // 屏幕规格
- changeSpec(e) {
- const ratio = e.split('*');
- this.specHeight = Number(ratio[0]);
- this.specWidth = Number(ratio[1]);
- },
- // 偶数行数据反转
- toSnakeData(pixelData, cols = 24) {
- const rows = []
- for (let i = 0; i < pixelData.length; i += cols) {
- let row = pixelData.slice(i, i + cols)
- if ((rows.length + 1) % 2 === 0) {
- row.reverse() // 偶数行反转
- }
- rows.push(row)
- }
- return rows
- },
- // 二维数组转字符串
- mergeRowsToString(rows) {
- const rowStrings = rows.map(r => r.join('')) // 每行合并
- return rowStrings.join('') // 所有行合并
- },
- // 生成一帧动画其他帧数据
- generateFrames(data, frameCount = 24) {
- const cols = this.specWidth; // 每行 24 个
- const rows = this.specHeight; // 共 12 行
- const frames = [];
- // 拆分成 12 行
- 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 = [];
- // 每行左移 f 次
- 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;
- },
- clickTempleteAdd2() {
-
- uni.showLoading({
- title: '添加中',
- mask: true
- });
-
- templeteAdd({
- name: '',
- userId: uni.getStorageSync('userId'),
- screen: '', //屏幕
- fpsTotal: '', //总帧数
- speed: '', //速度
- pic: '',
- fpsData: ''
- }).then(res => {
- console.log('添加动画模版', res)
- uni.hideLoading();
- uni.navigateBack({
- delta: 1
- });
- })
-
- },
- clickTempleteAdd() {
- if (!this.tempName) {
- uni.showToast({
- title: '请输入模版名称',
- duration: 2000,
- icon: 'none'
- });
- return
- }
- if (this.fileList1.length == 0) {
- uni.showToast({
- title: '请上传封面图片',
- duration: 2000,
- icon: 'none'
- });
- return
- }
- if (this.fpsData.length == 0) {
- uni.showToast({
- title: '请编辑动画帧',
- duration: 2000,
- icon: 'none'
- });
- return
- }
- //console.log('fpsData------------', this.fpsData)
- // 循环校验每一帧是否完整
- // let missingIndex = -1;
- // for (let i = 0; i < this.total; i++) {
- // if (this.fpsData[i] === null || this.fpsData[i] === undefined) {
- // missingIndex = i;
- // break;
- // }
- // }
- // if (missingIndex !== -1) {
- // uni.showToast({
- // title: '请编辑第' + (missingIndex + 1) + '帧',
- // duration: 2000,
- // icon: 'none'
- // });
- // return;
- // }
-
- uni.showLoading({
- title: '添加中',
- mask: true
- });
- // 动画方式
- if (this.modeVal == 0) { //一帧动画
- let frames = this.generateFrames(this.fpsData[0], 24);
- const parsedData = frames.map(item => {
- return this.toSnakeData(item, this.specWidth)
- });
- this.animationData = parsedData.map(item => {
- return this.mergeRowsToString(item)
- });
- this.fpsTotal = 24
- } else if (this.modeVal == 1) {
- const parsedData = this.fpsData.map(item => {
- return this.toSnakeData(item, this.specWidth)
- });
- this.animationData = parsedData.map(item => {
- return this.mergeRowsToString(item)
- });
- this.fpsTotal = this.total
- }
-
- // setTimeout(() => {
-
- // }, 1000)
- const addData = {
- name: this.tempName,
- userId: uni.getStorageSync('userId'),
- screen: this.specVal, //屏幕
- fpsTotal: this.fpsTotal, //总帧数
- speed: this.speed, //速度
- pic: this.fileList1[0].url,
- fpsData: JSON.stringify(this.animationData)
- }
- console.log('addData-----', addData)
- templeteAdd(addData).then(res => {
- console.log('添加动画模版', res)
- uni.hideLoading();
- if (res.code == 200) {
- uni.navigateBack({
- delta: 1
- });
- } else {
- uni.showToast({
- title: res.msg,
- duration: 1500,
- icon: 'none',
- mask: true
- });
- }
- })
-
- },
- clickSpeed() {
- this.$refs.pickerSpeed.open();
- },
- confirmSpeed(e) {
- console.log('confirmSpeed', e.value[0]);
- this.speed = e.value[0]
- },
- clickTotal() {
- if (this.total != 1) {
- this.$refs.pickerTotal.open();
- }
- },
- confirmTotal(e) {
- console.log('confirmTotal', e.value[0]);
- this.total = e.value[0]
- // this.fpsData = []
- // for (let i = 0; i < this.total; i++) {
- // this.fpsData.push(null);
- // }
- this.adjustFpsData()
- console.log('fpsData------------', this.fpsData)
- },
- changeMode(e) {
- this.fpsData = []
- if (e == 0) {
- this.total = 1
- } else {
- this.total = 2
- }
- this.adjustFpsData()
- },
- clickGoAnim(index) {
- uni.navigateTo({
- url: '/pages/animation/animation?fps=' + index
- })
- },
- adjustFpsData() {
- // 如果 fpsData 太长 → 截取
- if (this.fpsData.length > this.total) {
- this.fpsData = this.fpsData.slice(0, this.total);
- }
- // 如果 fpsData 太短 → 补齐 null
- else if (this.fpsData.length < this.total) {
- const missing = this.total - this.fpsData.length;
- this.fpsData = this.fpsData.concat(new Array(missing).fill(null));
- }
- },
- // 删除图片
- deletePic(event) {
- this[`fileList${event.name}`].splice(event.index, 1)
- },
- // 新增图片
- async afterRead(event) {
- console.log('event---', event)
- // 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
- let lists = [].concat(event.file)
- let fileListLen = this[`fileList${event.name}`].length
- lists.map((item) => {
- this[`fileList${event.name}`].push({
- ...item,
- status: 'uploading',
- message: '上传中'
- })
- })
- for (let i = 0; i < lists.length; i++) {
- const result = await this.uploadFilePromise(lists[i].url)
- this.avatar = result
- let item = this[`fileList${event.name}`][fileListLen]
- this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
- status: 'success',
- message: '',
- url: result
- }))
- fileListLen++
- }
- },
- uploadFilePromise(url) {
- return new Promise((resolve, reject) => {
- let a = uni.uploadFile({
- url: this.baseURL + '/common/uploadFileByAnny', // 仅为示例,非真实的接口地址
- filePath: url,
- name: 'file',
- success: (res) => {
- console.log('图片-----', JSON.parse(res.data).fileName)
- setTimeout(() => {
- resolve(JSON.parse(res.data).fileName)
- }, 1000)
- }
- });
- })
- }
- }
- }
-</script>
-
-<style lang="scss" scoped>
- .footer_none {
- height: 200rpx;
- }
-
- .footer_btn {
- position: fixed;
- left: 0;
- right: 0;
- bottom: 0;
- background: #fff;
- padding: 30rpx 40rpx;
- }
-
- .temp_btn {
- position: fixed;
- left: 30rpx;
- bottom: 60rpx;
- right: 30rpx;
- box-sizing: border-box;
- height: 96rpx;
- background: linear-gradient(142deg, #6FD2FF 0%, #268DFF 100%);
- border-radius: 385rpx 385rpx 385rpx 385rpx;
- font-size: 30rpx;
- color: #FFFFFF;
- line-height: 96rpx;
- text-align: center;
- }
-
- .spec_radio {
- margin-top: 30rpx;
-
- &:first-child {
- margin-top: 10rpx;
- }
- }
-
- .temp {
- padding: 0 30rpx;
-
- .temp_item1 {
- display: flex;
- align-items: center;
- justify-content: space-between;
- background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
- box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
- border-radius: 23rpx 23rpx 23rpx 23rpx;
- border: 2rpx solid #FFFFFF;
- padding: 30rpx;
- box-sizing: border-box;
- margin-top: 24rpx;
-
- .title {
- flex-shrink: 0;
- font-size: 30rpx;
- color: rgba(0, 0, 0, 0.9);
- line-height: 40rpx;
-
- }
-
- .item {}
- }
-
- .temp_item {
- background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
- box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
- border-radius: 23rpx 23rpx 23rpx 23rpx;
- border: 2rpx solid #FFFFFF;
- padding: 30rpx;
- box-sizing: border-box;
- margin-top: 24rpx;
-
- .title {
- flex-shrink: 0;
- font-size: 30rpx;
- color: rgba(0, 0, 0, 0.9);
- line-height: 40rpx;
- }
-
- .item {
- margin-top: 20rpx;
-
- &.item_pic {
- margin-top: 25rpx;
- }
- }
- }
- }
-
- .page_bg {
- width: 100%;
- min-height: calc(100vh - var(--window-bottom));
- background: url('/static/img/bg_2.jpg') no-repeat center top;
- background-size: 100% auto;
- }
-</style>
\ No newline at end of file
diff --git a/ledApp/pages/addTemplate/addTemplate copy 3.vue b/ledApp/pages/addTemplate/addTemplate copy 3.vue
deleted file mode 100644
index dc6ca6d..0000000
--- a/ledApp/pages/addTemplate/addTemplate copy 3.vue
+++ /dev/null
@@ -1,1056 +0,0 @@
-<template>
- <view class="page_bg">
-
- <uv-navbar :title="titleName" :autoBack="true" :placeholder="true" leftIconColor="#fff" bgColor="rgba(0,0,0,0)" titleStyle="color:#fff"></uv-navbar>
-
- <view class="temp">
- <view class="temp_item">
- <view class="title">
- 模版名称
- </view>
- <view class="item">
- <uv-input placeholder="请输入名称" border="none" v-model="tempName" fontSize="27rpx"
- customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
- </view>
- </view>
- <view class="temp_item">
- <view class="title">
- 封面图片
- </view>
- <view class="item item_pic">
- <uv-upload :fileList="fileList1" name="1" :maxCount="1" width="200rpx" height="200rpx"
- :previewFullImage="false" @afterRead="afterRead" @delete="deletePic">
- <!-- <image v-if="fileList1.length != 0"
- :src="baseURL + fileList1[0]?.url"
- mode="aspectFill"
- style="width: 200rpx;height: 200rpx;"
- ></image>
- <view class="item_pic_btn" v-else>
- <uv-icon name="camera-fill" color="#ccc" size="60rpx"></uv-icon>
- </view> -->
- </uv-upload>
- </view>
- </view>
- <view class="temp_item">
- <view class="title">
- 屏幕规格
- </view>
- <view class="item">
- <uv-radio-group v-model="specVal" placement="column" @change="changeSpec">
- <uv-radio class="spec_radio" v-for="(item, index) in specList" :key="index" :label="item.name"
- :name="item.name">
- </uv-radio>
- </uv-radio-group>
- </view>
- </view>
- <view class="temp_item">
- <view class="title">
- 动画方式
- </view>
- <view class="item">
- <uv-radio-group v-model="modeVal" placement="column" @change="changeMode">
- <uv-radio class="spec_radio" v-for="(item, index) in modeList" :key="index" :label="item.name"
- :name="item.val">
- </uv-radio>
- </uv-radio-group>
- </view>
- </view>
-
- <view class="temp_item">
- <view class="title">
- 动画帧数
- </view>
- <view class="item" @click="clickTotal">
- <uv-input placeholder="请输入" readonly border="none" v-model="total" suffixIcon="arrow-right"
- suffixIconStyle="color: #bbb" fontSize="26rpx"
- customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
- </view>
- </view>
- <view class="temp_item">
- <view class="title">
- 动画速度
- </view>
- <view class="item" @click="clickSpeed">
- <uv-input placeholder="请输入" readonly border="none" v-model="speed" suffixIcon="arrow-right"
- suffixIconStyle="color: #bbb" fontSize="26rpx"
- customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
- </view>
- </view>
- <!-- <view class="temp_item" v-if="modeVal == 1">
- <view class="title">
- 循环次数
- </view>
- <view class="item">
- <uv-input placeholder="请输入" readonly border="none" v-model="cycle" suffixIcon="arrow-right"
- suffixIconStyle="color: #bbb" fontSize="26rpx" customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
- </view>
- </view> -->
- <view class="temp_item1" v-for="(item,index) in total" :key="index">
- <view class="title">
- 动画第{{item}}帧
- </view>
- <view class="item">
- <uv-button text="编辑" @click="popupAnimationOpen(index)" shape="circle" size="small" color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)"></uv-button>
- </view>
- </view>
- </view>
-
- <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>
- </view>
-
- <!-- 动画帧数 -->
- <uv-picker ref="pickerTotal" :columns="columnsTotal" @confirm="confirmTotal"></uv-picker>
-
- <!-- 动画速度 -->
- <uv-picker ref="pickerSpeed" :columns="columnsSpeed" @confirm="confirmSpeed"></uv-picker>
-
- <!-- 动画弹出框 -->
- <uv-popup ref="popupAnimation" mode="center" @change="changeAnimation" :safeAreaInsetTop="false" :safeAreaInsetBottom="false" :zoom="false" duration="0">
- <view class="animation_pixel">
- <view class="back_btn" @click="popupAnimationClose">
- <uv-icon name="arrow-leftward" color="#333" size="30"></uv-icon>
- </view>
- <!-- 动画按钮 -->
- <view class="controls">
- <uv-button type="primary" :plain="true" text="分辨率" @click="changeRatio()"></uv-button>
- <uv-button type="primary" :plain="true" text="颜色" @click="changeColor()"></uv-button>
- <uv-button type="primary" :plain="true" text="添加文字" @click="addText()"></uv-button>
- <uv-button type="primary" :plain="true" text="清除" @click="clear()"></uv-button>
- <uv-button type="primary" :plain="true" text="保存" @click="clickSaveData"></uv-button>
- </view>
-
- <!-- 文字生成图形 -->
- <canvas canvas-id="myCanvas" id="myCanvas" class="canvas" :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"></canvas>
-
- <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>
- </view>
- </view>
- </view>
- </uv-popup>
-
- <!-- 分辨率 -->
- <uv-picker ref="pickerRatio" :columns="columnsRatio" @confirm="confirmRatio"></uv-picker>
-
- <!-- 颜色 -->
- <uv-popup ref="pickerColor" mode="bottom" round="10" :closeable="true">
- <view class="color_list">
- <view class="color_item" v-for="(item, index) in colorList" :class="{ hover: color === item }" :key="index" :style="{ backgroundColor: item }" @click="clickColor(item)"></view>
- </view>
- </uv-popup>
-
- </view>
-</template>
-
-<script>
- import {
- templeteAdd,
- templeteInfo,
- templeteEdit
- } from '@/api/api.js'
- let ctx = null
- export default {
- data() {
- return {
- tempName: '',
- pic: '',
- specList: [{
- name: '12*24'
- }, {
- name: '16*24'
- }, {
- name: '18*24'
- }],
- specVal: '12*24',
- specWidth: 24,
- specHeight: 12,
- modeList: [{
- name: '单个动画帧',
- val: 0
- },
- {
- name: '动画帧切换',
- val: 1
- }
- ],
- modeVal: 0,
- columnsTotal: [
- [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
- 28, 29, 30
- ]
- ],
- total: 1, //动画总帧数
- columnsSpeed: [
- [10, 20, 30, 40, 50, 60, 70, 80, 90]
- ],
- speed: 10,
- cycle: 1,
- fpsData: [],
- fpsIndex: 1, //第几帧
- fpsIds: [],
-
- baseURL: '',
- fileList1: [],
- animationData: [],
- fpsTotal: 1,
-
-
-
- // 模版动画
- pixelData: [],
- canvasData: [],
- canvasWidth: 240,
- canvasHeight: 120,
- width: 24,
- height: 12,
- color: '#FF0000',
- gridRect: null, // 存储像素网格的位置信息
- lastCell: null, // 防止重复绘制
- columnsRatio: [
- ['12*24', '16*32', '24*48', '32*64', '40*80']
- ],
- // columnsRatio: [
- // ['12*24']
- // ],
- colorList: [
- '#FF0000',
- '#00FF00',
- '#0000FF',
- '#FFFF00',
- '#FFA500',
- '#800080',
- '#00FFFF',
- '#FFC0CB'
- ],
- fpsIndex: 0,
- fpsList: [],
- tId: '',
- tempInfo:{},
- titleName: '新增模版'
-
- }
- },
- onLoad(opt) {
- this.baseURL = uni.$uv.http.config.baseURL
- if(opt.id){
- this.tId = opt.id
- this.getTempleteInfo()
- this.titleName = '编辑模版'
- }
- else{
- this.titleName = '新增模版'
- }
- },
- onShow() {
-
- },
- onHide() {
- this.$refs.popupAnimation.close();
- if (typeof plus !== 'undefined') {
- plus.screen.lockOrientation('portrait')
- }
- },
- onUnload() {
- if (typeof plus !== 'undefined') {
- plus.screen.lockOrientation('portrait')
- }
- },
- methods: {
- // 获取编辑详情
- getTempleteInfo(){
- templeteInfo(this.tId).then(res=>{
- console.log('获取编辑详情-------',res)
- if(res.code == 200){
- this.tempInfo = res.data
- this.tempName = this.tempInfo.name
- this.fileList1.push({
- url: this.baseURL + this.tempInfo.pic
- })
-
- this.specVal = this.tempInfo.screen
- this.modeVal = this.tempInfo.mode
-
- this.total = this.tempInfo.cycle
- this.fpsTotal = this.tempInfo.fpsTotal
- this.speed = this.tempInfo.speed
-
- this.fpsList = JSON.parse(this.tempInfo.fpsList)
-
- }
- else{
- uni.showToast({
- title: res.msg,
- duration: 2000,
- icon: 'none'
- });
- }
- })
- },
- // 添加模版提交接口
- clickTempleteAdd() {
-
- if (!this.tempName) {
- uni.showToast({
- title: '请输入模版名称',
- duration: 2000,
- icon: 'none'
- });
- return
- }
- if (this.fileList1.length == 0) {
- uni.showToast({
- title: '请上传封面图片',
- duration: 2000,
- icon: 'none'
- });
- return
- }
-
- if (this.fpsList.length == 0) {
- uni.showToast({
- title: '请编辑动画帧',
- duration: 2000,
- icon: 'none'
- });
- return
- } else {
- for (let i = 0; i < this.fpsList.length; i++) {
- if (this.fpsList[i] === null) {
- uni.showToast({
- title: `请编辑第 ${i+1} 个动画帧`,
- duration: 2000,
- icon: 'none'
- });
- return
- }
- }
- }
- console.log('this.fpsList=========',this.fpsList)
-
- uni.showLoading({
- title: '添加中',
- mask: true
- });
-
-
- // 遍历二维数组
- this.fpsData = this.fpsList.map(inner =>
- inner.map(item => {
- if (item === "") {
- return "000000"; // 空的改成000000
- } else if (item.includes("#")) {
- return item.replace("#", ""); // 去掉#
- } else {
- return item; // 保持原样
- }
- })
- );
-
- console.log('this.fpsData=========',this.fpsData)
-
- // 动画方式
- if (this.modeVal == 0) { //一帧动画
- let frames = this.generateFrames(this.fpsData[0], 24);
- const parsedData = frames.map(item => {
- return this.toSnakeData(item, this.specWidth)
- });
- this.animationData = parsedData.map(item => {
- return this.mergeRowsToString(item)
- });
- this.fpsTotal = 24
- } else if (this.modeVal == 1) {
- console.log('modeVal--------1')
- const parsedData = this.fpsData.map(item => {
- return this.toSnakeData(item, this.specWidth)
- });
- console.log('modeVal--------1',parsedData)
- this.animationData = parsedData.map(item => {
- return this.mergeRowsToString(item)
- });
- this.fpsTotal = this.total
- }
- console.log('animationData--------', this.animationData)
-
- // 判断this.fileList1[0].url里是否有 this.baseURL
- if (this.fileList1[0].url.includes(this.baseURL)) {
- this.fileList1[0].url = this.fileList1[0].url.replace(this.baseURL, "");
- }
-
- console.log('url----',this.fileList1[0].url);
-
- // 判断如果有id就编辑
- if(this.tId){
- const putData = {
- id: this.tId,
- name: this.tempName,
- userId: uni.getStorageSync('userId'),
- screen: this.specVal, //屏幕
- mode: this.modeVal,
- cycle: this.total,
- fpsTotal: this.fpsTotal, //总帧数
- speed: this.speed, //速度
- pic: this.fileList1[0].url,
- fpsData: JSON.stringify(this.animationData),
- fpsList: JSON.stringify(this.fpsList)
- }
- console.log('putData-----', putData)
- templeteEdit(putData).then(res => {
- console.log('编辑动画模版', res)
- uni.hideLoading();
- if (res.code == 200) {
- uni.navigateBack({
- delta: 1
- });
- } else {
- uni.showToast({
- title: res.msg,
- duration: 1500,
- icon: 'none',
- mask: true
- });
- }
- })
- }
- else{
- const addData = {
- name: this.tempName,
- userId: uni.getStorageSync('userId'),
- screen: this.specVal, //屏幕
- mode: this.modeVal,
- cycle: this.total,
- fpsTotal: this.fpsTotal, //总帧数
- speed: this.speed, //速度
- pic: this.fileList1[0].url,
- fpsData: JSON.stringify(this.animationData),
- fpsList: JSON.stringify(this.fpsList)
- }
- console.log('addData-----', addData)
- templeteAdd(addData).then(res => {
- console.log('添加动画模版', res)
- uni.hideLoading();
- if (res.code == 200) {
- uni.navigateBack({
- delta: 1
- });
- } else {
- uni.showToast({
- title: res.msg,
- duration: 1500,
- icon: 'none',
- mask: true
- });
- }
- })
- }
-
- },
- // 保存动画
- clickSaveData() {
-
- this.fpsList[this.fpsIndex] = this.pixelData;
-
-
-
-
- this.$refs.popupAnimation.close();
- },
- changeColor() {
- this.$refs.pickerColor.open();
- },
- clickColor(item) {
- console.log('confirmColor---', item);
- this.color = item;
- this.$refs.pickerColor.close();
- },
- changeRatio() {
- this.$refs.pickerRatio.open();
- },
- // 分辨率
- confirmRatio(e) {
- console.log('confirmRatio---', e.value[0]);
- const ratio = e.value[0].split('*');
- console.log('ratio---', ratio[0]);
- this.height = Number(ratio[0]);
- this.width = Number(ratio[1]);
- this.canvasWidth = this.width * 10;
- this.canvasHeight = this.height * 10;
- // 重新初始化一维数组
- this.pixelData = new Array(this.height * this.width).fill('')
- console.log('pixelData---', this.pixelData);
- },
- // 预先获取像素网格的位置信息
- getGridRect(cb) {
- if (this.gridRect) {
- cb(this.gridRect)
- } else {
- uni.createSelectorQuery().in(this).select('.pixel_grid').boundingClientRect(rect => {
- this.gridRect = rect
- cb(rect)
- }).exec()
- }
- },
- // 计算触摸点对应的格子
- getCellByTouch(touch, cb) {
- this.getGridRect(rect => {
- if (!rect) return
- const x = touch.clientX - rect.left
- const y = touch.clientY - rect.top
- const cellWidth = rect.width / this.width
- const cellHeight = rect.height / this.height
- const colIndex = Math.floor(x / cellWidth)
- const rowIndex = Math.floor(y / cellHeight)
- if (
- colIndex >= 0 && colIndex < this.width &&
- rowIndex >= 0 && rowIndex < this.height
- ) {
- cb({
- rowIndex,
- colIndex
- })
- }
- })
- },
- handleTouchStart(e) {
- // 阻止默认行为
- e.preventDefault && e.preventDefault();
-
- if (!e.touches.length) return
- this.getCellByTouch(e.touches[0], cell => {
- this.lastCell = cell
- this.handleClick(cell.rowIndex, cell.colIndex)
- })
- },
- handleTouchMove(e) {
- // 阻止默认行为
- e.preventDefault && e.preventDefault();
-
- if (!e.touches.length) return
- this.getCellByTouch(e.touches[0], cell => {
- // 只在格子变化时才触发
- if (!this.lastCell || this.lastCell.rowIndex !== cell.rowIndex || this.lastCell.colIndex !==
- cell.colIndex) {
- this.lastCell = cell
- this.handleClick(cell.rowIndex, cell.colIndex)
- }
- })
- },
- handleTouchEnd() {
- this.lastCell = null
- },
- handleClick(rowIndex, colIndex) {
- console.log(rowIndex, colIndex)
- // 计算一维数组的索引
- const index = rowIndex * this.width + colIndex
- // 如果当前像素有颜色,则清除;如果没有颜色,则添加当前选择的颜色
- const currentColor = this.pixelData[index]
- const newColor = currentColor ? '' : this.color
- this.$set(this.pixelData, index, newColor)
- },
- clear() {
- ctx && ctx.clearRect(0, 0, 1000, 1000)
- // 将所有像素点设置为空字符串
- this.pixelData = new Array(this.height * this.width).fill('')
- },
- // 模版动画弹出框
- changeAnimation(e){
- //console.log('打开关闭模版动画弹窗触发---',e)
- if(e.show){
- if (typeof plus !== 'undefined') {
- plus.screen.lockOrientation('landscape-primary')
- }
- }
- else{
- if (typeof plus !== 'undefined') {
- plus.screen.lockOrientation('portrait')
- }
- }
- },
- // 打开模版动画
- popupAnimationOpen(index){
- this.$refs.popupAnimation.open();
- this.fpsIndex = index
-
- if(this.fpsList[this.fpsIndex] == null){
- console.log('第一次编辑-----null')
- this.pixelData = new Array(this.height * this.width).fill('')
- }else{
- console.log('已编辑过-----')
- this.pixelData = this.fpsList[this.fpsIndex]
- }
- },
- // 关闭模版动画
- popupAnimationClose(){
- this.$refs.popupAnimation.close();
- this.pixelData = new Array(this.height * this.width).fill('')
- },
- // 屏幕规格
- changeSpec(e) {
- const ratio = e.split('*');
- this.specHeight = Number(ratio[0]);
- this.specWidth = Number(ratio[1]);
- },
- // 偶数行数据反转
- toSnakeData(pixelData, cols = 24) {
- const rows = []
- for (let i = 0; i < pixelData.length; i += cols) {
- let row = pixelData.slice(i, i + cols)
- if ((rows.length + 1) % 2 === 0) {
- row.reverse() // 偶数行反转
- }
- rows.push(row)
- }
- return rows
- },
- // 二维数组转字符串
- mergeRowsToString(rows) {
- const rowStrings = rows.map(r => r.join('')) // 每行合并
- return rowStrings.join('') // 所有行合并
- },
- // 生成一帧动画其他帧数据
- generateFrames(data, frameCount = 24) {
- const cols = this.specWidth; // 每行 24 个
- const rows = this.specHeight; // 共 12 行
- const frames = [];
- // 拆分成 12 行
- 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 = [];
- // 每行左移 f 次
- 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;
- },
-
- clickSpeed() {
- this.$refs.pickerSpeed.open();
- },
- confirmSpeed(e) {
- console.log('confirmSpeed', e.value[0]);
- this.speed = e.value[0]
- },
- clickTotal() {
- if (this.total != 1) {
- this.$refs.pickerTotal.open();
- }
- },
- confirmTotal(e) {
- console.log('confirmTotal', e.value[0]);
- this.total = e.value[0]
- this.adjustFpsData()
- },
- changeMode(e) {
- this.fpsList = []
- this.fpsData = []
- if (e == 0) {
- this.total = 1
- } else {
- this.total = 2
- }
- this.adjustFpsData()
- },
- adjustFpsData() {
- // 如果 fpsData 太长 → 截取
- if (this.fpsList.length > this.total) {
- this.fpsList = this.fpsList.slice(0, this.total);
- }
- // 如果 fpsData 太短 → 补齐 null
- else if (this.fpsList.length < this.total) {
- const missing = this.total - this.fpsList.length;
- this.fpsList = this.fpsList.concat(new Array(missing).fill(null));
- }
-
- // 如果 fpsList 太长 → 截取
- if (this.fpsData.length > this.total) {
- this.fpsData = this.fpsData.slice(0, this.total);
- }
- // 如果 fpsList 太短 → 补齐 null
- else if (this.fpsData.length < this.total) {
- const missing = this.total - this.fpsData.length;
- this.fpsData = this.fpsData.concat(new Array(missing).fill(null));
- }
- },
- // 删除图片
- deletePic(event) {
- this[`fileList${event.name}`].splice(event.index, 1)
- },
- // 新增图片
- async afterRead(event) {
- console.log('event---', event)
- // 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
- let lists = [].concat(event.file)
- let fileListLen = this[`fileList${event.name}`].length
- lists.map((item) => {
- this[`fileList${event.name}`].push({
- ...item,
- status: 'uploading',
- message: '上传中'
- })
- })
- for (let i = 0; i < lists.length; i++) {
- const result = await this.uploadFilePromise(lists[i].url)
- this.avatar = result
- let item = this[`fileList${event.name}`][fileListLen]
- this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
- status: 'success',
- message: '',
- url: result
- }))
- fileListLen++
- }
- },
- uploadFilePromise(url) {
- return new Promise((resolve, reject) => {
- let a = uni.uploadFile({
- url: this.baseURL + '/common/uploadFileByAnny', // 仅为示例,非真实的接口地址
- filePath: url,
- name: 'file',
- success: (res) => {
- console.log('图片-----', JSON.parse(res.data))
- setTimeout(() => {
- resolve(JSON.parse(res.data).fileName)
- }, 1000)
- }
- });
- })
- },
- // 生成点阵数据
- generateDotMatrix(char) {
- return new Promise((resolve, reject) => {
-
- // 使用 uni-app 的 canvas context
- ctx = uni.createCanvasContext('myCanvas', this);
-
- // 设置字体样式
- ctx.setFontSize(this.canvasHeight * 0.9);
- ctx.setFillStyle('#000')
- ctx.setTextAlign('center')
- ctx.setTextBaseline('middle')
- // 绘制文字
- // 计算文字位置
- // 水平居中
- const x = this.canvasWidth / 2;
- // 垂直居中,稍微向上偏移一点
- const y = this.canvasHeight / 2;
-
- // 绘制文字
- ctx.fillText(char, x, y);
-
- // 绘制到画布
- ctx.draw(false, () => {
- // 获取画布数据
- uni.canvasGetImageData({
- canvasId: 'myCanvas',
- x: 0,
- y: 0,
- width: this.canvasWidth,
- height: this.canvasHeight,
- success: (res) => {
- console.log(res)
- const imageData = res.data;
- // 生成一维数组
- const pixelArray = new Array(this.height * this.width).fill('')
-
- // 计算每个格子的像素大小
- const cellWidth = this.canvasWidth / this.width;
- const cellHeight = this.canvasHeight / this.height;
-
- // 生成一维数组
- for (let y = 0; y < this.height; y++) {
- for (let x = 0; x < this.width; x++) {
- let blackPixels = 0;
- let totalPixels = 0;
-
- // 对每个格子进行采样
- for (let py = 0; py < cellHeight; py++) {
- for (let px = 0; px < cellWidth; px++) {
- const pixelY = Math.floor(y * cellHeight + py);
- const pixelX = Math.floor(x * cellWidth + px);
-
- const idx = (pixelY * this.canvasWidth + pixelX) * 4;
- const r = imageData[idx];
- const g = imageData[idx + 1];
- const b = imageData[idx + 2];
- const a = imageData[idx + 3];
-
- // 计算亮度
- const brightness = 0.3 * r + 0.59 * g + 0.11 * b;
-
- // 如果亮度小于阈值且透明度大于阈值,则认为是黑色
- if (brightness < 128 && a > 128) {
- blackPixels++;
- }
- totalPixels++;
- }
- }
-
- // 如果黑色像素占比超过阈值,则设置为当前选择的颜色
- const isBlack = blackPixels / totalPixels > 0.3;
- const arrayIndex = y * this.width + x;
- pixelArray[arrayIndex] = isBlack ? this.color : ''; // 改为颜色值或空字符串
- }
- }
-
- resolve(pixelArray);
- },
- fail: (err) => {
- reject(err);
- }
- });
- });
- });
- },
-
- // 修改添加文字方法
- async addText() {
- uni.showModal({
- title: '添加文字',
- content: '',
- editable: true,
- placeholderText: '请输入文字',
- success: async (res) => {
- if (res.confirm) {
- const text = res.content;
- if (text.length > 0) {
- try {
- // 生成点阵数据
- const dotMatrix = await this.generateDotMatrix(text);
- console.log('点阵数据:', dotMatrix);
- this.pixelData = dotMatrix
- } catch (error) {
- console.error('生成点阵失败:', error);
- uni.showToast({
- title: '生成点阵失败',
- icon: 'none'
- });
- }
- } else {
- uni.showToast({
- title: '请输入文字',
- icon: 'none'
- });
- }
- }
- }
- });
- },
- }
- }
-</script>
-
-<style lang="scss" scoped>
- page {
- -webkit-text-size-adjust: 100%;
- text-size-adjust: 100%;
- }
- .item_pic_btn{
- width: 200rpx;
- height: 200rpx;
- background: #eee;
- display: flex;
- align-items: center;
- justify-content: center;
- margin-bottom: 15rpx;
- }
- .color_list {
- width: 100%;
- display: flex;
- justify-content: center;
- flex-wrap: wrap;
- gap: 30rpx;
- box-sizing: border-box;
- padding: 50rpx;
-
- .color_item {
- width: 30rpx;
- height: 30rpx;
- border-radius: 5rpx;
- }
- }
-
- .controls {
- position: absolute;
- bottom: 10px;
- left: 50px;
- display: flex;
- gap: 20px;
- z-index: 9;
- }
- .animation_pixel{
- width: 100vw;
- height: 100vh;
- position: relative;
- .back_btn{
- position: absolute;
- left: 30px;
- top: 20px;
- z-index: 9999;
- }
- .canvas {
- margin: 100rpx auto 0;
- background: #ccc;
- position: absolute;
- z-index: 1;
- left: -9999px;
- top: -9999px;
- }
- .pixel_grid {
- position: fixed;
- left: 0;
- top: var(--status-bar-height);
- right: 0;
- bottom: 0;
- display: grid;
- gap: 1px;
- box-sizing: border-box;
- background: #eee;
- touch-action: none;
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- user-select: none;
-
- .pixel_cell {
- display: flex;
- align-items: center;
- justify-content: center;
- box-sizing: border-box;
- background: #fff;
-
- .pixel_cell_item {
- width: 50%;
- height: 50%;
- aspect-ratio: 1/1;
- font-size: 10rpx;
- }
- }
- }
-
- }
- // -------------
- .footer_none {
- height: 200rpx;
- }
-
- .footer_btn {
- position: fixed;
- left: 0;
- right: 0;
- bottom: 0;
- background: #fff;
- padding: 30rpx 40rpx;
- }
- .footer_btn2 {
- position: fixed;
- left: 0;
- right: 0;
- bottom: 0;
- background: #fff;
- padding: 30rpx 40rpx;
- display: flex;
- align-items: center;
- justify-content: space-between;
- box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1);
- }
-
- .temp_btn {
- position: fixed;
- left: 30rpx;
- bottom: 60rpx;
- right: 30rpx;
- box-sizing: border-box;
- height: 96rpx;
- background: linear-gradient(142deg, #6FD2FF 0%, #268DFF 100%);
- border-radius: 385rpx 385rpx 385rpx 385rpx;
- font-size: 30rpx;
- color: #FFFFFF;
- line-height: 96rpx;
- text-align: center;
- }
-
- .spec_radio {
- margin-top: 30rpx;
-
- &:first-child {
- margin-top: 10rpx;
- }
- }
-
- .temp {
- padding: 0 30rpx;
-
- .temp_item1 {
- display: flex;
- align-items: center;
- justify-content: space-between;
- background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
- box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
- border-radius: 23rpx 23rpx 23rpx 23rpx;
- border: 2rpx solid #FFFFFF;
- padding: 30rpx;
- box-sizing: border-box;
- margin-top: 24rpx;
-
- .title {
- flex-shrink: 0;
- font-size: 30rpx;
- color: rgba(0, 0, 0, 0.9);
- line-height: 40rpx;
-
- }
-
- .item {}
- }
-
- .temp_item {
- display: flex;
- align-items: flex-start;
- justify-content: flex-start;
- flex-direction: column;
- background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
- box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
- border-radius: 23rpx 23rpx 23rpx 23rpx;
- border: 2rpx solid #FFFFFF;
- padding: 30rpx;
- box-sizing: border-box;
- margin-top: 24rpx;
-
- .title {
- flex-shrink: 0;
- font-size: 30rpx;
- color: rgba(0, 0, 0, 0.9);
- line-height: 40rpx;
- }
-
- .item {
- margin-top: 20rpx;
- width: 100%;
- &.item_pic {
- margin-top: 25rpx;
- }
- }
- }
- }
-
- .page_bg {
- width: 100%;
- min-height: calc(100vh - var(--window-bottom));
- background: url('/static/img/bg_2.jpg') no-repeat center top;
- background-size: 100% auto;
- }
-</style>
\ No newline at end of file
diff --git a/ledApp/pages/addTemplate/addTemplate copy 4.vue b/ledApp/pages/addTemplate/addTemplate copy 4.vue
deleted file mode 100644
index dc6ca6d..0000000
--- a/ledApp/pages/addTemplate/addTemplate copy 4.vue
+++ /dev/null
@@ -1,1056 +0,0 @@
-<template>
- <view class="page_bg">
-
- <uv-navbar :title="titleName" :autoBack="true" :placeholder="true" leftIconColor="#fff" bgColor="rgba(0,0,0,0)" titleStyle="color:#fff"></uv-navbar>
-
- <view class="temp">
- <view class="temp_item">
- <view class="title">
- 模版名称
- </view>
- <view class="item">
- <uv-input placeholder="请输入名称" border="none" v-model="tempName" fontSize="27rpx"
- customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
- </view>
- </view>
- <view class="temp_item">
- <view class="title">
- 封面图片
- </view>
- <view class="item item_pic">
- <uv-upload :fileList="fileList1" name="1" :maxCount="1" width="200rpx" height="200rpx"
- :previewFullImage="false" @afterRead="afterRead" @delete="deletePic">
- <!-- <image v-if="fileList1.length != 0"
- :src="baseURL + fileList1[0]?.url"
- mode="aspectFill"
- style="width: 200rpx;height: 200rpx;"
- ></image>
- <view class="item_pic_btn" v-else>
- <uv-icon name="camera-fill" color="#ccc" size="60rpx"></uv-icon>
- </view> -->
- </uv-upload>
- </view>
- </view>
- <view class="temp_item">
- <view class="title">
- 屏幕规格
- </view>
- <view class="item">
- <uv-radio-group v-model="specVal" placement="column" @change="changeSpec">
- <uv-radio class="spec_radio" v-for="(item, index) in specList" :key="index" :label="item.name"
- :name="item.name">
- </uv-radio>
- </uv-radio-group>
- </view>
- </view>
- <view class="temp_item">
- <view class="title">
- 动画方式
- </view>
- <view class="item">
- <uv-radio-group v-model="modeVal" placement="column" @change="changeMode">
- <uv-radio class="spec_radio" v-for="(item, index) in modeList" :key="index" :label="item.name"
- :name="item.val">
- </uv-radio>
- </uv-radio-group>
- </view>
- </view>
-
- <view class="temp_item">
- <view class="title">
- 动画帧数
- </view>
- <view class="item" @click="clickTotal">
- <uv-input placeholder="请输入" readonly border="none" v-model="total" suffixIcon="arrow-right"
- suffixIconStyle="color: #bbb" fontSize="26rpx"
- customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
- </view>
- </view>
- <view class="temp_item">
- <view class="title">
- 动画速度
- </view>
- <view class="item" @click="clickSpeed">
- <uv-input placeholder="请输入" readonly border="none" v-model="speed" suffixIcon="arrow-right"
- suffixIconStyle="color: #bbb" fontSize="26rpx"
- customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
- </view>
- </view>
- <!-- <view class="temp_item" v-if="modeVal == 1">
- <view class="title">
- 循环次数
- </view>
- <view class="item">
- <uv-input placeholder="请输入" readonly border="none" v-model="cycle" suffixIcon="arrow-right"
- suffixIconStyle="color: #bbb" fontSize="26rpx" customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
- </view>
- </view> -->
- <view class="temp_item1" v-for="(item,index) in total" :key="index">
- <view class="title">
- 动画第{{item}}帧
- </view>
- <view class="item">
- <uv-button text="编辑" @click="popupAnimationOpen(index)" shape="circle" size="small" color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)"></uv-button>
- </view>
- </view>
- </view>
-
- <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>
- </view>
-
- <!-- 动画帧数 -->
- <uv-picker ref="pickerTotal" :columns="columnsTotal" @confirm="confirmTotal"></uv-picker>
-
- <!-- 动画速度 -->
- <uv-picker ref="pickerSpeed" :columns="columnsSpeed" @confirm="confirmSpeed"></uv-picker>
-
- <!-- 动画弹出框 -->
- <uv-popup ref="popupAnimation" mode="center" @change="changeAnimation" :safeAreaInsetTop="false" :safeAreaInsetBottom="false" :zoom="false" duration="0">
- <view class="animation_pixel">
- <view class="back_btn" @click="popupAnimationClose">
- <uv-icon name="arrow-leftward" color="#333" size="30"></uv-icon>
- </view>
- <!-- 动画按钮 -->
- <view class="controls">
- <uv-button type="primary" :plain="true" text="分辨率" @click="changeRatio()"></uv-button>
- <uv-button type="primary" :plain="true" text="颜色" @click="changeColor()"></uv-button>
- <uv-button type="primary" :plain="true" text="添加文字" @click="addText()"></uv-button>
- <uv-button type="primary" :plain="true" text="清除" @click="clear()"></uv-button>
- <uv-button type="primary" :plain="true" text="保存" @click="clickSaveData"></uv-button>
- </view>
-
- <!-- 文字生成图形 -->
- <canvas canvas-id="myCanvas" id="myCanvas" class="canvas" :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"></canvas>
-
- <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>
- </view>
- </view>
- </view>
- </uv-popup>
-
- <!-- 分辨率 -->
- <uv-picker ref="pickerRatio" :columns="columnsRatio" @confirm="confirmRatio"></uv-picker>
-
- <!-- 颜色 -->
- <uv-popup ref="pickerColor" mode="bottom" round="10" :closeable="true">
- <view class="color_list">
- <view class="color_item" v-for="(item, index) in colorList" :class="{ hover: color === item }" :key="index" :style="{ backgroundColor: item }" @click="clickColor(item)"></view>
- </view>
- </uv-popup>
-
- </view>
-</template>
-
-<script>
- import {
- templeteAdd,
- templeteInfo,
- templeteEdit
- } from '@/api/api.js'
- let ctx = null
- export default {
- data() {
- return {
- tempName: '',
- pic: '',
- specList: [{
- name: '12*24'
- }, {
- name: '16*24'
- }, {
- name: '18*24'
- }],
- specVal: '12*24',
- specWidth: 24,
- specHeight: 12,
- modeList: [{
- name: '单个动画帧',
- val: 0
- },
- {
- name: '动画帧切换',
- val: 1
- }
- ],
- modeVal: 0,
- columnsTotal: [
- [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
- 28, 29, 30
- ]
- ],
- total: 1, //动画总帧数
- columnsSpeed: [
- [10, 20, 30, 40, 50, 60, 70, 80, 90]
- ],
- speed: 10,
- cycle: 1,
- fpsData: [],
- fpsIndex: 1, //第几帧
- fpsIds: [],
-
- baseURL: '',
- fileList1: [],
- animationData: [],
- fpsTotal: 1,
-
-
-
- // 模版动画
- pixelData: [],
- canvasData: [],
- canvasWidth: 240,
- canvasHeight: 120,
- width: 24,
- height: 12,
- color: '#FF0000',
- gridRect: null, // 存储像素网格的位置信息
- lastCell: null, // 防止重复绘制
- columnsRatio: [
- ['12*24', '16*32', '24*48', '32*64', '40*80']
- ],
- // columnsRatio: [
- // ['12*24']
- // ],
- colorList: [
- '#FF0000',
- '#00FF00',
- '#0000FF',
- '#FFFF00',
- '#FFA500',
- '#800080',
- '#00FFFF',
- '#FFC0CB'
- ],
- fpsIndex: 0,
- fpsList: [],
- tId: '',
- tempInfo:{},
- titleName: '新增模版'
-
- }
- },
- onLoad(opt) {
- this.baseURL = uni.$uv.http.config.baseURL
- if(opt.id){
- this.tId = opt.id
- this.getTempleteInfo()
- this.titleName = '编辑模版'
- }
- else{
- this.titleName = '新增模版'
- }
- },
- onShow() {
-
- },
- onHide() {
- this.$refs.popupAnimation.close();
- if (typeof plus !== 'undefined') {
- plus.screen.lockOrientation('portrait')
- }
- },
- onUnload() {
- if (typeof plus !== 'undefined') {
- plus.screen.lockOrientation('portrait')
- }
- },
- methods: {
- // 获取编辑详情
- getTempleteInfo(){
- templeteInfo(this.tId).then(res=>{
- console.log('获取编辑详情-------',res)
- if(res.code == 200){
- this.tempInfo = res.data
- this.tempName = this.tempInfo.name
- this.fileList1.push({
- url: this.baseURL + this.tempInfo.pic
- })
-
- this.specVal = this.tempInfo.screen
- this.modeVal = this.tempInfo.mode
-
- this.total = this.tempInfo.cycle
- this.fpsTotal = this.tempInfo.fpsTotal
- this.speed = this.tempInfo.speed
-
- this.fpsList = JSON.parse(this.tempInfo.fpsList)
-
- }
- else{
- uni.showToast({
- title: res.msg,
- duration: 2000,
- icon: 'none'
- });
- }
- })
- },
- // 添加模版提交接口
- clickTempleteAdd() {
-
- if (!this.tempName) {
- uni.showToast({
- title: '请输入模版名称',
- duration: 2000,
- icon: 'none'
- });
- return
- }
- if (this.fileList1.length == 0) {
- uni.showToast({
- title: '请上传封面图片',
- duration: 2000,
- icon: 'none'
- });
- return
- }
-
- if (this.fpsList.length == 0) {
- uni.showToast({
- title: '请编辑动画帧',
- duration: 2000,
- icon: 'none'
- });
- return
- } else {
- for (let i = 0; i < this.fpsList.length; i++) {
- if (this.fpsList[i] === null) {
- uni.showToast({
- title: `请编辑第 ${i+1} 个动画帧`,
- duration: 2000,
- icon: 'none'
- });
- return
- }
- }
- }
- console.log('this.fpsList=========',this.fpsList)
-
- uni.showLoading({
- title: '添加中',
- mask: true
- });
-
-
- // 遍历二维数组
- this.fpsData = this.fpsList.map(inner =>
- inner.map(item => {
- if (item === "") {
- return "000000"; // 空的改成000000
- } else if (item.includes("#")) {
- return item.replace("#", ""); // 去掉#
- } else {
- return item; // 保持原样
- }
- })
- );
-
- console.log('this.fpsData=========',this.fpsData)
-
- // 动画方式
- if (this.modeVal == 0) { //一帧动画
- let frames = this.generateFrames(this.fpsData[0], 24);
- const parsedData = frames.map(item => {
- return this.toSnakeData(item, this.specWidth)
- });
- this.animationData = parsedData.map(item => {
- return this.mergeRowsToString(item)
- });
- this.fpsTotal = 24
- } else if (this.modeVal == 1) {
- console.log('modeVal--------1')
- const parsedData = this.fpsData.map(item => {
- return this.toSnakeData(item, this.specWidth)
- });
- console.log('modeVal--------1',parsedData)
- this.animationData = parsedData.map(item => {
- return this.mergeRowsToString(item)
- });
- this.fpsTotal = this.total
- }
- console.log('animationData--------', this.animationData)
-
- // 判断this.fileList1[0].url里是否有 this.baseURL
- if (this.fileList1[0].url.includes(this.baseURL)) {
- this.fileList1[0].url = this.fileList1[0].url.replace(this.baseURL, "");
- }
-
- console.log('url----',this.fileList1[0].url);
-
- // 判断如果有id就编辑
- if(this.tId){
- const putData = {
- id: this.tId,
- name: this.tempName,
- userId: uni.getStorageSync('userId'),
- screen: this.specVal, //屏幕
- mode: this.modeVal,
- cycle: this.total,
- fpsTotal: this.fpsTotal, //总帧数
- speed: this.speed, //速度
- pic: this.fileList1[0].url,
- fpsData: JSON.stringify(this.animationData),
- fpsList: JSON.stringify(this.fpsList)
- }
- console.log('putData-----', putData)
- templeteEdit(putData).then(res => {
- console.log('编辑动画模版', res)
- uni.hideLoading();
- if (res.code == 200) {
- uni.navigateBack({
- delta: 1
- });
- } else {
- uni.showToast({
- title: res.msg,
- duration: 1500,
- icon: 'none',
- mask: true
- });
- }
- })
- }
- else{
- const addData = {
- name: this.tempName,
- userId: uni.getStorageSync('userId'),
- screen: this.specVal, //屏幕
- mode: this.modeVal,
- cycle: this.total,
- fpsTotal: this.fpsTotal, //总帧数
- speed: this.speed, //速度
- pic: this.fileList1[0].url,
- fpsData: JSON.stringify(this.animationData),
- fpsList: JSON.stringify(this.fpsList)
- }
- console.log('addData-----', addData)
- templeteAdd(addData).then(res => {
- console.log('添加动画模版', res)
- uni.hideLoading();
- if (res.code == 200) {
- uni.navigateBack({
- delta: 1
- });
- } else {
- uni.showToast({
- title: res.msg,
- duration: 1500,
- icon: 'none',
- mask: true
- });
- }
- })
- }
-
- },
- // 保存动画
- clickSaveData() {
-
- this.fpsList[this.fpsIndex] = this.pixelData;
-
-
-
-
- this.$refs.popupAnimation.close();
- },
- changeColor() {
- this.$refs.pickerColor.open();
- },
- clickColor(item) {
- console.log('confirmColor---', item);
- this.color = item;
- this.$refs.pickerColor.close();
- },
- changeRatio() {
- this.$refs.pickerRatio.open();
- },
- // 分辨率
- confirmRatio(e) {
- console.log('confirmRatio---', e.value[0]);
- const ratio = e.value[0].split('*');
- console.log('ratio---', ratio[0]);
- this.height = Number(ratio[0]);
- this.width = Number(ratio[1]);
- this.canvasWidth = this.width * 10;
- this.canvasHeight = this.height * 10;
- // 重新初始化一维数组
- this.pixelData = new Array(this.height * this.width).fill('')
- console.log('pixelData---', this.pixelData);
- },
- // 预先获取像素网格的位置信息
- getGridRect(cb) {
- if (this.gridRect) {
- cb(this.gridRect)
- } else {
- uni.createSelectorQuery().in(this).select('.pixel_grid').boundingClientRect(rect => {
- this.gridRect = rect
- cb(rect)
- }).exec()
- }
- },
- // 计算触摸点对应的格子
- getCellByTouch(touch, cb) {
- this.getGridRect(rect => {
- if (!rect) return
- const x = touch.clientX - rect.left
- const y = touch.clientY - rect.top
- const cellWidth = rect.width / this.width
- const cellHeight = rect.height / this.height
- const colIndex = Math.floor(x / cellWidth)
- const rowIndex = Math.floor(y / cellHeight)
- if (
- colIndex >= 0 && colIndex < this.width &&
- rowIndex >= 0 && rowIndex < this.height
- ) {
- cb({
- rowIndex,
- colIndex
- })
- }
- })
- },
- handleTouchStart(e) {
- // 阻止默认行为
- e.preventDefault && e.preventDefault();
-
- if (!e.touches.length) return
- this.getCellByTouch(e.touches[0], cell => {
- this.lastCell = cell
- this.handleClick(cell.rowIndex, cell.colIndex)
- })
- },
- handleTouchMove(e) {
- // 阻止默认行为
- e.preventDefault && e.preventDefault();
-
- if (!e.touches.length) return
- this.getCellByTouch(e.touches[0], cell => {
- // 只在格子变化时才触发
- if (!this.lastCell || this.lastCell.rowIndex !== cell.rowIndex || this.lastCell.colIndex !==
- cell.colIndex) {
- this.lastCell = cell
- this.handleClick(cell.rowIndex, cell.colIndex)
- }
- })
- },
- handleTouchEnd() {
- this.lastCell = null
- },
- handleClick(rowIndex, colIndex) {
- console.log(rowIndex, colIndex)
- // 计算一维数组的索引
- const index = rowIndex * this.width + colIndex
- // 如果当前像素有颜色,则清除;如果没有颜色,则添加当前选择的颜色
- const currentColor = this.pixelData[index]
- const newColor = currentColor ? '' : this.color
- this.$set(this.pixelData, index, newColor)
- },
- clear() {
- ctx && ctx.clearRect(0, 0, 1000, 1000)
- // 将所有像素点设置为空字符串
- this.pixelData = new Array(this.height * this.width).fill('')
- },
- // 模版动画弹出框
- changeAnimation(e){
- //console.log('打开关闭模版动画弹窗触发---',e)
- if(e.show){
- if (typeof plus !== 'undefined') {
- plus.screen.lockOrientation('landscape-primary')
- }
- }
- else{
- if (typeof plus !== 'undefined') {
- plus.screen.lockOrientation('portrait')
- }
- }
- },
- // 打开模版动画
- popupAnimationOpen(index){
- this.$refs.popupAnimation.open();
- this.fpsIndex = index
-
- if(this.fpsList[this.fpsIndex] == null){
- console.log('第一次编辑-----null')
- this.pixelData = new Array(this.height * this.width).fill('')
- }else{
- console.log('已编辑过-----')
- this.pixelData = this.fpsList[this.fpsIndex]
- }
- },
- // 关闭模版动画
- popupAnimationClose(){
- this.$refs.popupAnimation.close();
- this.pixelData = new Array(this.height * this.width).fill('')
- },
- // 屏幕规格
- changeSpec(e) {
- const ratio = e.split('*');
- this.specHeight = Number(ratio[0]);
- this.specWidth = Number(ratio[1]);
- },
- // 偶数行数据反转
- toSnakeData(pixelData, cols = 24) {
- const rows = []
- for (let i = 0; i < pixelData.length; i += cols) {
- let row = pixelData.slice(i, i + cols)
- if ((rows.length + 1) % 2 === 0) {
- row.reverse() // 偶数行反转
- }
- rows.push(row)
- }
- return rows
- },
- // 二维数组转字符串
- mergeRowsToString(rows) {
- const rowStrings = rows.map(r => r.join('')) // 每行合并
- return rowStrings.join('') // 所有行合并
- },
- // 生成一帧动画其他帧数据
- generateFrames(data, frameCount = 24) {
- const cols = this.specWidth; // 每行 24 个
- const rows = this.specHeight; // 共 12 行
- const frames = [];
- // 拆分成 12 行
- 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 = [];
- // 每行左移 f 次
- 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;
- },
-
- clickSpeed() {
- this.$refs.pickerSpeed.open();
- },
- confirmSpeed(e) {
- console.log('confirmSpeed', e.value[0]);
- this.speed = e.value[0]
- },
- clickTotal() {
- if (this.total != 1) {
- this.$refs.pickerTotal.open();
- }
- },
- confirmTotal(e) {
- console.log('confirmTotal', e.value[0]);
- this.total = e.value[0]
- this.adjustFpsData()
- },
- changeMode(e) {
- this.fpsList = []
- this.fpsData = []
- if (e == 0) {
- this.total = 1
- } else {
- this.total = 2
- }
- this.adjustFpsData()
- },
- adjustFpsData() {
- // 如果 fpsData 太长 → 截取
- if (this.fpsList.length > this.total) {
- this.fpsList = this.fpsList.slice(0, this.total);
- }
- // 如果 fpsData 太短 → 补齐 null
- else if (this.fpsList.length < this.total) {
- const missing = this.total - this.fpsList.length;
- this.fpsList = this.fpsList.concat(new Array(missing).fill(null));
- }
-
- // 如果 fpsList 太长 → 截取
- if (this.fpsData.length > this.total) {
- this.fpsData = this.fpsData.slice(0, this.total);
- }
- // 如果 fpsList 太短 → 补齐 null
- else if (this.fpsData.length < this.total) {
- const missing = this.total - this.fpsData.length;
- this.fpsData = this.fpsData.concat(new Array(missing).fill(null));
- }
- },
- // 删除图片
- deletePic(event) {
- this[`fileList${event.name}`].splice(event.index, 1)
- },
- // 新增图片
- async afterRead(event) {
- console.log('event---', event)
- // 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
- let lists = [].concat(event.file)
- let fileListLen = this[`fileList${event.name}`].length
- lists.map((item) => {
- this[`fileList${event.name}`].push({
- ...item,
- status: 'uploading',
- message: '上传中'
- })
- })
- for (let i = 0; i < lists.length; i++) {
- const result = await this.uploadFilePromise(lists[i].url)
- this.avatar = result
- let item = this[`fileList${event.name}`][fileListLen]
- this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
- status: 'success',
- message: '',
- url: result
- }))
- fileListLen++
- }
- },
- uploadFilePromise(url) {
- return new Promise((resolve, reject) => {
- let a = uni.uploadFile({
- url: this.baseURL + '/common/uploadFileByAnny', // 仅为示例,非真实的接口地址
- filePath: url,
- name: 'file',
- success: (res) => {
- console.log('图片-----', JSON.parse(res.data))
- setTimeout(() => {
- resolve(JSON.parse(res.data).fileName)
- }, 1000)
- }
- });
- })
- },
- // 生成点阵数据
- generateDotMatrix(char) {
- return new Promise((resolve, reject) => {
-
- // 使用 uni-app 的 canvas context
- ctx = uni.createCanvasContext('myCanvas', this);
-
- // 设置字体样式
- ctx.setFontSize(this.canvasHeight * 0.9);
- ctx.setFillStyle('#000')
- ctx.setTextAlign('center')
- ctx.setTextBaseline('middle')
- // 绘制文字
- // 计算文字位置
- // 水平居中
- const x = this.canvasWidth / 2;
- // 垂直居中,稍微向上偏移一点
- const y = this.canvasHeight / 2;
-
- // 绘制文字
- ctx.fillText(char, x, y);
-
- // 绘制到画布
- ctx.draw(false, () => {
- // 获取画布数据
- uni.canvasGetImageData({
- canvasId: 'myCanvas',
- x: 0,
- y: 0,
- width: this.canvasWidth,
- height: this.canvasHeight,
- success: (res) => {
- console.log(res)
- const imageData = res.data;
- // 生成一维数组
- const pixelArray = new Array(this.height * this.width).fill('')
-
- // 计算每个格子的像素大小
- const cellWidth = this.canvasWidth / this.width;
- const cellHeight = this.canvasHeight / this.height;
-
- // 生成一维数组
- for (let y = 0; y < this.height; y++) {
- for (let x = 0; x < this.width; x++) {
- let blackPixels = 0;
- let totalPixels = 0;
-
- // 对每个格子进行采样
- for (let py = 0; py < cellHeight; py++) {
- for (let px = 0; px < cellWidth; px++) {
- const pixelY = Math.floor(y * cellHeight + py);
- const pixelX = Math.floor(x * cellWidth + px);
-
- const idx = (pixelY * this.canvasWidth + pixelX) * 4;
- const r = imageData[idx];
- const g = imageData[idx + 1];
- const b = imageData[idx + 2];
- const a = imageData[idx + 3];
-
- // 计算亮度
- const brightness = 0.3 * r + 0.59 * g + 0.11 * b;
-
- // 如果亮度小于阈值且透明度大于阈值,则认为是黑色
- if (brightness < 128 && a > 128) {
- blackPixels++;
- }
- totalPixels++;
- }
- }
-
- // 如果黑色像素占比超过阈值,则设置为当前选择的颜色
- const isBlack = blackPixels / totalPixels > 0.3;
- const arrayIndex = y * this.width + x;
- pixelArray[arrayIndex] = isBlack ? this.color : ''; // 改为颜色值或空字符串
- }
- }
-
- resolve(pixelArray);
- },
- fail: (err) => {
- reject(err);
- }
- });
- });
- });
- },
-
- // 修改添加文字方法
- async addText() {
- uni.showModal({
- title: '添加文字',
- content: '',
- editable: true,
- placeholderText: '请输入文字',
- success: async (res) => {
- if (res.confirm) {
- const text = res.content;
- if (text.length > 0) {
- try {
- // 生成点阵数据
- const dotMatrix = await this.generateDotMatrix(text);
- console.log('点阵数据:', dotMatrix);
- this.pixelData = dotMatrix
- } catch (error) {
- console.error('生成点阵失败:', error);
- uni.showToast({
- title: '生成点阵失败',
- icon: 'none'
- });
- }
- } else {
- uni.showToast({
- title: '请输入文字',
- icon: 'none'
- });
- }
- }
- }
- });
- },
- }
- }
-</script>
-
-<style lang="scss" scoped>
- page {
- -webkit-text-size-adjust: 100%;
- text-size-adjust: 100%;
- }
- .item_pic_btn{
- width: 200rpx;
- height: 200rpx;
- background: #eee;
- display: flex;
- align-items: center;
- justify-content: center;
- margin-bottom: 15rpx;
- }
- .color_list {
- width: 100%;
- display: flex;
- justify-content: center;
- flex-wrap: wrap;
- gap: 30rpx;
- box-sizing: border-box;
- padding: 50rpx;
-
- .color_item {
- width: 30rpx;
- height: 30rpx;
- border-radius: 5rpx;
- }
- }
-
- .controls {
- position: absolute;
- bottom: 10px;
- left: 50px;
- display: flex;
- gap: 20px;
- z-index: 9;
- }
- .animation_pixel{
- width: 100vw;
- height: 100vh;
- position: relative;
- .back_btn{
- position: absolute;
- left: 30px;
- top: 20px;
- z-index: 9999;
- }
- .canvas {
- margin: 100rpx auto 0;
- background: #ccc;
- position: absolute;
- z-index: 1;
- left: -9999px;
- top: -9999px;
- }
- .pixel_grid {
- position: fixed;
- left: 0;
- top: var(--status-bar-height);
- right: 0;
- bottom: 0;
- display: grid;
- gap: 1px;
- box-sizing: border-box;
- background: #eee;
- touch-action: none;
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- user-select: none;
-
- .pixel_cell {
- display: flex;
- align-items: center;
- justify-content: center;
- box-sizing: border-box;
- background: #fff;
-
- .pixel_cell_item {
- width: 50%;
- height: 50%;
- aspect-ratio: 1/1;
- font-size: 10rpx;
- }
- }
- }
-
- }
- // -------------
- .footer_none {
- height: 200rpx;
- }
-
- .footer_btn {
- position: fixed;
- left: 0;
- right: 0;
- bottom: 0;
- background: #fff;
- padding: 30rpx 40rpx;
- }
- .footer_btn2 {
- position: fixed;
- left: 0;
- right: 0;
- bottom: 0;
- background: #fff;
- padding: 30rpx 40rpx;
- display: flex;
- align-items: center;
- justify-content: space-between;
- box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1);
- }
-
- .temp_btn {
- position: fixed;
- left: 30rpx;
- bottom: 60rpx;
- right: 30rpx;
- box-sizing: border-box;
- height: 96rpx;
- background: linear-gradient(142deg, #6FD2FF 0%, #268DFF 100%);
- border-radius: 385rpx 385rpx 385rpx 385rpx;
- font-size: 30rpx;
- color: #FFFFFF;
- line-height: 96rpx;
- text-align: center;
- }
-
- .spec_radio {
- margin-top: 30rpx;
-
- &:first-child {
- margin-top: 10rpx;
- }
- }
-
- .temp {
- padding: 0 30rpx;
-
- .temp_item1 {
- display: flex;
- align-items: center;
- justify-content: space-between;
- background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
- box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
- border-radius: 23rpx 23rpx 23rpx 23rpx;
- border: 2rpx solid #FFFFFF;
- padding: 30rpx;
- box-sizing: border-box;
- margin-top: 24rpx;
-
- .title {
- flex-shrink: 0;
- font-size: 30rpx;
- color: rgba(0, 0, 0, 0.9);
- line-height: 40rpx;
-
- }
-
- .item {}
- }
-
- .temp_item {
- display: flex;
- align-items: flex-start;
- justify-content: flex-start;
- flex-direction: column;
- background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
- box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
- border-radius: 23rpx 23rpx 23rpx 23rpx;
- border: 2rpx solid #FFFFFF;
- padding: 30rpx;
- box-sizing: border-box;
- margin-top: 24rpx;
-
- .title {
- flex-shrink: 0;
- font-size: 30rpx;
- color: rgba(0, 0, 0, 0.9);
- line-height: 40rpx;
- }
-
- .item {
- margin-top: 20rpx;
- width: 100%;
- &.item_pic {
- margin-top: 25rpx;
- }
- }
- }
- }
-
- .page_bg {
- width: 100%;
- min-height: calc(100vh - var(--window-bottom));
- background: url('/static/img/bg_2.jpg') no-repeat center top;
- background-size: 100% auto;
- }
-</style>
\ No newline at end of file
diff --git a/ledApp/pages/addTemplate/addTemplate copy.vue b/ledApp/pages/addTemplate/addTemplate copy.vue
deleted file mode 100644
index 5778ff6..0000000
--- a/ledApp/pages/addTemplate/addTemplate copy.vue
+++ /dev/null
@@ -1,348 +0,0 @@
-<template>
- <view class="page_bg">
- <uv-navbar title="新增模版" :autoBack="true" :placeholder="true" leftIconColor="#fff" bgColor="rgba(0,0,0,0)" titleStyle="color:#fff"></uv-navbar>
-
- <view class="temp">
- <view class="temp_item">
- <view class="title">
- 模版名称
- </view>
- <view class="item">
- <uv-input placeholder="请输入名称" border="none" v-model="tempName" fontSize="27rpx" customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
- </view>
- </view>
- <view class="temp_item">
- <view class="title">
- 屏幕规格
- </view>
- <view class="item">
- <uv-radio-group v-model="specVal" placement="column">
- <uv-radio
- class="spec_radio"
- v-for="(item, index) in specList"
- :key="index"
- :label="item.name"
- :name="item.name">
- </uv-radio>
- </uv-radio-group>
- </view>
- </view>
- <view class="temp_item">
- <view class="title">
- 动画方式
- </view>
- <view class="item">
- <uv-radio-group v-model="modeVal" placement="column" @change="changeMode">
- <uv-radio
- class="spec_radio"
- v-for="(item, index) in modeList"
- :key="index"
- :label="item.name"
- :name="item.val">
- </uv-radio>
- </uv-radio-group>
- </view>
- </view>
-
- <view class="temp_item">
- <view class="title">
- 动画帧数
- </view>
- <view class="item" @click="clickTotal">
- <uv-input placeholder="请输入" readonly border="none" v-model="total" suffixIcon="arrow-right"
- suffixIconStyle="color: #bbb" fontSize="26rpx" customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
- </view>
- </view>
- <view class="temp_item" v-if="modeVal == 1">
- <view class="title">
- 动画速度
- </view>
- <view class="item" @click="clickSpeed">
- <uv-input placeholder="请输入" readonly border="none" v-model="speed" suffixIcon="arrow-right"
- suffixIconStyle="color: #bbb" fontSize="26rpx" customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
- </view>
- </view>
- <!-- <view class="temp_item" v-if="modeVal == 1">
- <view class="title">
- 循环次数
- </view>
- <view class="item">
- <uv-input placeholder="请输入" readonly border="none" v-model="cycle" suffixIcon="arrow-right"
- suffixIconStyle="color: #bbb" fontSize="26rpx" customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
- </view>
- </view> -->
-
- <view class="temp_item1" v-for="(item,index) in total" :key="index">
- <view class="title">
- 动画第{{item}}帧
- </view>
- <view class="item">
- <uv-button text="编辑" @click="clickGoAnim(item)" shape="circle" size="small" color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)"></uv-button>
- </view>
- </view>
-
- </view>
-
-
- <!-- <view class="temp_btn" @click="clickGoAnim">
- 编辑动画
- </view> -->
-
- <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>
- </view>
-
- <!-- 动画帧数 -->
- <uv-picker ref="pickerTotal" :columns="columnsTotal" @confirm="confirmTotal"></uv-picker>
-
- <!-- 动画速度 -->
- <uv-picker ref="pickerSpeed" :columns="columnsSpeed" @confirm="confirmSpeed"></uv-picker>
-
- </view>
-</template>
-
-<script>
- import { templeteAdd } from '@/api/api.js'
- export default {
- data() {
- return {
- tempName: '',
- specList: [{
- name: '12*24'
- }, {
- name: '16*24'
- }, {
- name: '18*24'
- }],
- specVal: '12*24',
- modeList: [
- {
- name: '单个动画帧',
- val: 0
- },
- {
- name: '动画帧切换',
- val: 1
- }
- ],
- modeVal: 0,
- columnsTotal: [
- [2,3,4,5,6,7,8,9]
- ],
- total: 1,
- columnsSpeed: [
- [10,20,30,40,50,60,70,80,90]
- ],
- speed: 10,
- cycle: 1,
- fpsData: [],
- fpsIndex: 1,
- fpsIds: []
- }
- },
- onLoad() {
-
- },
- onShow() {
- console.log('------onShow')
- const fpsParams = uni.getStorageSync('fpsParams');
- // if (fpsParams) {
- // console.log('收到回传参数:', fpsParams);
- // this.fpsIndex = fpsParams.fpsIndex+1
- // this.fpsData[fpsParams.fpsIndex] = fpsParams.data;
- // this.fpsIds.push(fpsParams.fpsIndex)
- // console.log(this.fpsIndex, this.fpsData);
- // uni.removeStorageSync('fpsParams'); // 清理掉
- // }
- if (fpsParams) {
- console.log('收到回传参数:', fpsParams);
- // 用 splice 代替下标赋值
- this.fpsIndex = fpsParams.fpsIndex+1
- this.fpsData.splice(fpsParams.fpsIndex, 1, fpsParams.data);
-
- if (!this.fpsIds.includes(fpsParams.fpsIndex + 1)) {
- this.fpsIds.push(fpsParams.fpsIndex + 1);
- }
- console.log(this.fpsIndex, this.fpsData);
- uni.removeStorageSync('fpsParams'); // 清理掉
- }
- },
- methods: {
- clickTempleteAdd(){
- if(!this.tempName){
- uni.showToast({
- title: '请输入模版名称',
- duration: 2000,
- icon: 'none'
- });
- return
- }
- if(this.fpsIds.length == 0){
- uni.showToast({
- title: '请编辑动画帧',
- duration: 2000,
- icon: 'none'
- });
- return
- }
- let totalArr = new Array(this.total).fill(0).map((_, i) => i + 1);
- let diffBA = totalArr.filter(v => !this.fpsIds.includes(v));
- console.log(totalArr, diffBA)
- if(diffBA.length != 0){
- uni.showToast({
- title: '请编辑第'+ Math.min(...diffBA) +'帧',
- duration: 2000,
- icon: 'none'
- });
- return
- }
-
- templeteAdd({
- name: this.tempName,
- userId: uni.getStorageSync('userId'),
- screen: this.specVal, //屏幕
- fpsTotal: this.total, //总帧数
- speed: this.speed, //速度
-
- fpsData: JSON.stringify(this.fpsData)
- }).then(res=>{
- console.log('添加动画模版',res)
- uni.showToast({
- title: res.msg,
- duration: 2000,
- icon: 'none'
- });
- if(res.code == 200){
- uni.navigateBack({
- delta: 1
- });
- }
- })
- },
- clickSpeed(){
- this.$refs.pickerSpeed.open();
- },
- confirmSpeed(e) {
- console.log('confirmSpeed', e.value[0]);
- this.speed = e.value[0]
- },
- clickTotal(){
- if(this.total != 1){
- this.$refs.pickerTotal.open();
- }
- },
- confirmTotal(e) {
- console.log('confirmTotal', e.value[0]);
- this.total = e.value[0]
- },
- changeMode(e){
- if(e == 0){
- this.total = 1
- }
- else{
- this.total = 2
- }
- this.fpsIndex = 1
- this.fpsData = []
- },
- clickGoAnim(index){
- console.log(this.fpsIndex, index)
- // if(this.fpsIndex > index){
- // uni.showToast({
- // title: '请先编辑第'+this.fpsIndex+'帧',
- // duration: 2000,
- // icon: 'none'
- // });
- // return
- // }
- uni.navigateTo({
- url: '/pages/animation/animation?fps='+index
- })
- }
- }
- }
-</script>
-
-<style lang="scss" scoped>
-.footer_none{
- height: 200rpx;
-}
-.footer_btn{
- position: fixed;
- left: 0;
- right: 0;
- bottom: 0;
- background: #fff;
- padding:30rpx 40rpx;
-}
-.temp_btn{
- position: fixed;
- left: 30rpx;
- bottom: 60rpx;
- right: 30rpx;
- box-sizing: border-box;
- height: 96rpx;
- background: linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%);
- border-radius: 385rpx 385rpx 385rpx 385rpx;
- font-size: 30rpx;
- color: #FFFFFF;
- line-height: 96rpx;
- text-align: center;
-}
-.spec_radio{
- margin-top: 30rpx;
- &:first-child{
- margin-top: 10rpx;
- }
-}
-.temp{
- padding:0 30rpx;
- .temp_item1{
- display: flex;
- align-items: center;
- justify-content: space-between;
- background: linear-gradient( 180deg, #FFFFFF 0%, rgba(255,255,255,0.5) 100%);
- box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67,147,248,0.1), -8rpx -8rpx 23rpx 0rpx rgba(255,255,255,0.4);
- border-radius: 23rpx 23rpx 23rpx 23rpx;
- border: 2rpx solid #FFFFFF;
- padding:30rpx;
- box-sizing: border-box;
- margin-top: 24rpx;
- .title{
- flex-shrink: 0;
- font-size: 30rpx;
- color: rgba(0,0,0,0.9);
- line-height: 40rpx;
-
- }
- .item{
-
- }
- }
- .temp_item{
- background: linear-gradient( 180deg, #FFFFFF 0%, rgba(255,255,255,0.5) 100%);
- box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67,147,248,0.1), -8rpx -8rpx 23rpx 0rpx rgba(255,255,255,0.4);
- border-radius: 23rpx 23rpx 23rpx 23rpx;
- border: 2rpx solid #FFFFFF;
- padding:30rpx;
- box-sizing: border-box;
- margin-top: 24rpx;
- .title{
- flex-shrink: 0;
- font-size: 30rpx;
- color: rgba(0,0,0,0.9);
- line-height: 40rpx;
- }
- .item{
- margin-top: 15rpx;
- }
- }
-}
-.page_bg{
- width: 100%;
- min-height: calc(100vh - var(--window-bottom));
- background:url('/static/img/bg_2.jpg') no-repeat center top;
- background-size: 100% auto;
-}
-</style>
diff --git a/ledApp/pages/addTemplate/addTemplate.vue b/ledApp/pages/addTemplate/addTemplate.vue
index 0a208de..ba8ff1d 100644
--- a/ledApp/pages/addTemplate/addTemplate.vue
+++ b/ledApp/pages/addTemplate/addTemplate.vue
@@ -264,13 +264,6 @@
}
},
methods: {
- // 跳转到编辑动画页面
- goToAnimaiton(index){
- this.fpsIndex = index
- uni.navigateTo({
- url: '/pages/animation/animation?index='+index
- })
- },
// 获取编辑详情
getTempleteInfo(){
templeteInfo(this.tId).then(res=>{
diff --git a/ledApp/pages/animation/animation copy.vue b/ledApp/pages/animation/animation copy.vue
deleted file mode 100644
index 15ccbff..0000000
--- a/ledApp/pages/animation/animation copy.vue
+++ /dev/null
@@ -1,533 +0,0 @@
-<template>
- <view>
- <!-- <uv-navbar title="动画编辑" :autoBack="true" bgColor="rgba(0,0,0,0)" :placeholder="false"></uv-navbar> -->
- <view class="back_btn" @click="clickBack">
- <uv-icon name="arrow-leftward" color="#333" size="26rpx"></uv-icon>
- </view>
- <view class="controls">
- <uv-button type="primary" :plain="true" text="分辨率" @click="changeRatio()"></uv-button>
- <uv-button type="primary" :plain="true" text="颜色" @click="changeColor()"></uv-button>
-
- <uv-button type="primary" :plain="true" text="添加文字" @click="addText()"></uv-button>
- <uv-button type="primary" :plain="true" text="清除" @click="clear()"></uv-button>
-
- <uv-button type="primary" :plain="true" text="保存" @click="clickSaveData"></uv-button>
- </view>
-
- <!-- 文字生成图形 -->
- <canvas canvas-id="myCanvas" id="myCanvas" class="canvas" :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"></canvas>
-
- <!-- 修改像素网格显示 -->
- <!-- <view class="pixel-grid" @touchstart.prevent="handleTouchStart" @touchmove.prevent="handleTouchMove">
- <view class="pixel-row" v-for="(row, rowIndex) in Math.ceil(pixelData.length / width)" :key="rowIndex">
- <view class="pixel-cell" v-for="(item, colIndex) in width" :key="colIndex">
- <view v-if="pixelData[rowIndex * width + colIndex]" class="pixel-cell-item" :style="{ backgroundColor: pixelData[rowIndex * width + colIndex] }">
- {{rowIndex,colIndex}}
- </view>
- </view>
- </view>
- </view> -->
-
- <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>
- </view>
- </view>
-
- <!-- 分辨率 -->
- <uv-picker ref="pickerRatio" :columns="columnsRatio" @confirm="confirmRatio"></uv-picker>
-
- <!-- 颜色 -->
- <uv-popup ref="pickerColor" mode="bottom" round="10" :closeable="true">
- <view class="color_list">
- <view class="color_item" v-for="(item, index) in colorList" :class="{ hover: color === item }"
- :key="index" :style="{ backgroundColor: item }" @click="clickColor(item)"></view>
- </view>
- </uv-popup>
-
- <!-- <uv-pick-color ref="pickerColor" @confirm="confirm"></uv-pick-color> -->
-
- </view>
-</template>
-
-<script>
- let ctx = null
-
- export default {
- data() {
- return {
- pixelData: [],
- canvasData: [],
- canvasWidth: 240,
- canvasHeight: 120,
- width: 24,
- height: 12,
- color: '#FF0000',
- gridRect: null, // 存储像素网格的位置信息
- lastCell: null, // 防止重复绘制
- columnsRatio: [
- ['12*24', '16*32', '24*48', '32*64', '40*80']
- ],
- // columnsRatio: [
- // ['12*24']
- // ],
- colorList: [
- '#FF0000',
- '#00FF00',
- '#0000FF',
- '#FFFF00',
- '#FFA500',
- '#800080',
- '#00FFFF',
- '#FFC0CB'
- ],
- fpsIndex: 0
- }
- },
- onLoad(opt) {
- // App端强制横屏
- if (typeof plus !== 'undefined') {
- plus.screen.lockOrientation('landscape-primary')
- }
- if (opt.fps) {
- this.fpsIndex = opt.fps
- }
-
- },
- onShow() {
- // 改为一维数组初始化
- this.pixelData = new Array(this.height * this.width).fill('')
- },
- onHide() {
- console.log('离开页面恢复竖屏------onHide')
- // 离开页面恢复竖屏
- if (typeof plus !== 'undefined') {
- plus.screen.lockOrientation('portrait')
- }
- },
- onUnload() {
- console.log('离开页面恢复竖屏------onUnload')
- // 离开页面恢复竖屏
- if (typeof plus !== 'undefined') {
- plus.screen.lockOrientation('portrait')
- }
- },
- methods: {
- clickBack() {
- uni.navigateBack({
- delta: 1
- });
- },
- clickSaveData() {
- const parsed = this.pixelData.map(item => {
- if (item) {
- return item.replace("#", "");
- } else {
- return "000000";
- }
- });
- console.log('parsed-------------', parsed.join(''))
- //const snakeRows = this.toSnakeData(parsed, this.width)
- //console.log('snakeRows-------------', snakeRows)
- //const snakeString = this.mergeRowsToString(snakeRows)
- //console.log('snakeString-------------', snakeString)
-
- // uni.setStorageSync('fpsParams', {
- // shouldRefresh: true,
- // fpsIndex: Number(this.fpsIndex),
- // //data: snakeString
- // data: parsed
- // });
-
- uni.setStorage({
- key: 'fpsParams',
- data: {
- shouldRefresh: true,
- fpsIndex: Number(this.fpsIndex),
- data: parsed
- },
- success: () => {
- console.log('存储成功');
- uni.navigateBack({
- delta: 1,
- });
- },
- fail: (err) => {
- console.error('存储失败:', err);
- }
- });
-
-
- },
- // 偶数行数据反转
- toSnakeData(pixelData, cols = 24) {
- const rows = []
- for (let i = 0; i < pixelData.length; i += cols) {
- let row = pixelData.slice(i, i + cols)
- if ((rows.length + 1) % 2 === 0) {
- row.reverse() // 偶数行反转
- }
- rows.push(row)
- }
- return rows
- },
- // 二维数组转字符串
- mergeRowsToString(rows) {
- const rowStrings = rows.map(r => r.join('')) // 每行合并
- return rowStrings.join('') // 所有行合并
- },
- getData() {
- uni.switchTab({
- url: '/pages/template/template',
- success() {
- console.log('跳转成功');
- },
- fail(err) {
- console.error('跳转失败:', err);
- }
- });
- },
- changeColor() {
- this.$refs.pickerColor.open();
- },
- clickColor(item) {
- console.log('confirmColor---', item);
- this.color = item;
- this.$refs.pickerColor.close();
- },
- changeRatio() {
- this.$refs.pickerRatio.open();
- },
- // 分辨率
- confirmRatio(e) {
- console.log('confirmRatio---', e.value[0]);
- const ratio = e.value[0].split('*');
- console.log('ratio---', ratio[0]);
- this.height = Number(ratio[0]);
- this.width = Number(ratio[1]);
- this.canvasWidth = this.width * 10;
- this.canvasHeight = this.height * 10;
- // 重新初始化一维数组
- this.pixelData = new Array(this.height * this.width).fill('')
- console.log('pixelData---', this.pixelData);
- },
- // 预先获取像素网格的位置信息
- getGridRect(cb) {
- if (this.gridRect) {
- cb(this.gridRect)
- } else {
- uni.createSelectorQuery().in(this).select('.pixel_grid').boundingClientRect(rect => {
- this.gridRect = rect
- cb(rect)
- }).exec()
- }
- },
- // 计算触摸点对应的格子
- getCellByTouch(touch, cb) {
- this.getGridRect(rect => {
- if (!rect) return
- const x = touch.clientX - rect.left
- const y = touch.clientY - rect.top
- const cellWidth = rect.width / this.width
- const cellHeight = rect.height / this.height
- const colIndex = Math.floor(x / cellWidth)
- const rowIndex = Math.floor(y / cellHeight)
- if (
- colIndex >= 0 && colIndex < this.width &&
- rowIndex >= 0 && rowIndex < this.height
- ) {
- cb({
- rowIndex,
- colIndex
- })
- }
- })
- },
- handleTouchStart(e) {
- // 阻止默认行为
- e.preventDefault && e.preventDefault();
-
- if (!e.touches.length) return
- this.getCellByTouch(e.touches[0], cell => {
- this.lastCell = cell
- this.handleClick(cell.rowIndex, cell.colIndex)
- })
- },
- handleTouchMove(e) {
- // 阻止默认行为
- e.preventDefault && e.preventDefault();
-
- if (!e.touches.length) return
- this.getCellByTouch(e.touches[0], cell => {
- // 只在格子变化时才触发
- if (!this.lastCell || this.lastCell.rowIndex !== cell.rowIndex || this.lastCell.colIndex !==
- cell.colIndex) {
- this.lastCell = cell
- this.handleClick(cell.rowIndex, cell.colIndex)
- }
- })
- },
- handleTouchEnd() {
- this.lastCell = null
- },
- handleClick(rowIndex, colIndex) {
- console.log(rowIndex, colIndex)
- // 计算一维数组的索引
- const index = rowIndex * this.width + colIndex
- // 如果当前像素有颜色,则清除;如果没有颜色,则添加当前选择的颜色
- const currentColor = this.pixelData[index]
- const newColor = currentColor ? '' : this.color
- this.$set(this.pixelData, index, newColor)
- },
- clear() {
- ctx && ctx.clearRect(0, 0, 1000, 1000)
- // 将所有像素点设置为空字符串
- this.pixelData = new Array(this.height * this.width).fill('')
- },
- // 生成点阵数据
- generateDotMatrix(char) {
- return new Promise((resolve, reject) => {
-
- // 使用 uni-app 的 canvas context
- ctx = uni.createCanvasContext('myCanvas', this);
-
- // 设置字体样式
- ctx.setFontSize(this.canvasHeight * 0.9);
- ctx.setFillStyle('#000')
- ctx.setTextAlign('center')
- ctx.setTextBaseline('middle')
- // 绘制文字
- // 计算文字位置
- // 水平居中
- const x = this.canvasWidth / 2;
- // 垂直居中,稍微向上偏移一点
- const y = this.canvasHeight / 2;
-
- // 绘制文字
- ctx.fillText(char, x, y);
-
- // 绘制到画布
- ctx.draw(false, () => {
- // 获取画布数据
- uni.canvasGetImageData({
- canvasId: 'myCanvas',
- x: 0,
- y: 0,
- width: this.canvasWidth,
- height: this.canvasHeight,
- success: (res) => {
- console.log(res)
- const imageData = res.data;
- // 生成一维数组
- const pixelArray = new Array(this.height * this.width).fill('')
-
- // 计算每个格子的像素大小
- const cellWidth = this.canvasWidth / this.width;
- const cellHeight = this.canvasHeight / this.height;
-
- // 生成一维数组
- for (let y = 0; y < this.height; y++) {
- for (let x = 0; x < this.width; x++) {
- let blackPixels = 0;
- let totalPixels = 0;
-
- // 对每个格子进行采样
- for (let py = 0; py < cellHeight; py++) {
- for (let px = 0; px < cellWidth; px++) {
- const pixelY = Math.floor(y * cellHeight + py);
- const pixelX = Math.floor(x * cellWidth + px);
-
- const idx = (pixelY * this.canvasWidth +
- pixelX) * 4;
- const r = imageData[idx];
- const g = imageData[idx + 1];
- const b = imageData[idx + 2];
- const a = imageData[idx + 3];
-
- // 计算亮度
- const brightness = 0.3 * r + 0.59 * g + 0.11 *
- b;
-
- // 如果亮度小于阈值且透明度大于阈值,则认为是黑色
- if (brightness < 128 && a > 128) {
- blackPixels++;
- }
- totalPixels++;
- }
- }
-
- // 如果黑色像素占比超过阈值,则设置为当前选择的颜色
- const isBlack = blackPixels / totalPixels > 0.3;
- const arrayIndex = y * this.width + x;
- pixelArray[arrayIndex] = isBlack ? this.color :
- ''; // 改为颜色值或空字符串
- }
- }
-
- resolve(pixelArray);
- },
- fail: (err) => {
- reject(err);
- }
- });
- });
- });
- },
-
- // 修改添加文字方法
- async addText() {
- uni.showModal({
- title: '添加文字',
- content: '',
- editable: true,
- placeholderText: '请输入文字',
- success: async (res) => {
- if (res.confirm) {
- const text = res.content;
- if (text.length > 0) {
- try {
- // 生成点阵数据
- const dotMatrix = await this.generateDotMatrix(text);
- console.log('点阵数据:', dotMatrix);
- this.pixelData = dotMatrix
- } catch (error) {
- console.error('生成点阵失败:', error);
- uni.showToast({
- title: '生成点阵失败',
- icon: 'none'
- });
- }
- } else {
- uni.showToast({
- title: '请输入文字',
- icon: 'none'
- });
- }
- }
- }
- });
- },
-
-
- }
- }
-</script>
-
-<style lang="scss" scoped>
- .back_btn {
- position: fixed;
- left: 10rpx;
- top: 26rpx;
- z-index: 9999;
- }
-
- .pixel_grid {
- position: fixed;
- left: 0;
- top: var(--status-bar-height);
- right: 0;
- bottom: 0;
- display: grid;
- gap: 1px;
- box-sizing: border-box;
- background: #eee;
- touch-action: none;
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- user-select: none;
-
- .pixel_cell {
- display: flex;
- align-items: center;
- justify-content: center;
- box-sizing: border-box;
- background: #fff;
-
- .pixel_cell_item {
- width: 50%;
- height: 50%;
- aspect-ratio: 1/1;
- font-size: 10rpx;
- }
- }
- }
-
- .color_list {
- width: 100%;
- display: flex;
- justify-content: center;
- flex-wrap: wrap;
- gap: 30rpx;
- box-sizing: border-box;
- padding: 50rpx;
-
- .color_item {
- width: 30rpx;
- height: 30rpx;
- border-radius: 5rpx;
- }
- }
-
- .controls {
- position: absolute;
- bottom: 15rpx;
- left: 20rpx;
- display: flex;
- gap: 20rpx;
- z-index: 9;
- }
-
- .canvas {
- margin: 100rpx auto 0;
- background: #ccc;
- position: absolute;
- z-index: 1;
- left: -9999px;
- top: -9999px;
- }
-
- .pixel-grid {
- position: fixed;
- left: 0;
- top: 0;
- right: 0;
- bottom: 0;
- display: flex;
- flex-direction: column;
- z-index: 8;
- background: #fff;
- touch-action: none;
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- user-select: none;
-
- .pixel-row {
- display: flex;
- flex: 1;
-
- .pixel-cell {
- flex: 1;
- display: flex;
- justify-content: center;
- align-items: center;
- border: 1rpx solid #eee;
- box-sizing: border-box;
-
- .pixel-cell-item {
- width: 50%;
- aspect-ratio: 1/1;
- color: #333 !important;
- ;
- font-size: 10rpx;
- }
- }
- }
- }
-</style>
\ No newline at end of file
diff --git a/ledApp/pages/animation/animation.vue b/ledApp/pages/animation/animation.vue
deleted file mode 100644
index 280e105..0000000
--- a/ledApp/pages/animation/animation.vue
+++ /dev/null
@@ -1,624 +0,0 @@
-<template>
- <view>
- <view class="animation_pixel">
- <view class="back_btn" @click="popupAnimationClose">
- <uv-icon name="arrow-leftward" color="#333" size="30"></uv-icon>
- </view>
- <!-- 动画按钮 -->
- <view class="controls">
- <uv-button type="primary" :plain="true" text="分辨率" @click="changeRatio()"></uv-button>
- <uv-button type="primary" :plain="true" text="颜色" @click="changeColor()"></uv-button>
- <uv-button type="primary" :plain="true" text="添加文字" @click="addText()"></uv-button>
- <uv-button type="primary" :plain="true" text="清除" @click="clear()"></uv-button>
- <uv-button type="primary" :plain="true" text="保存" @click="clickSaveData"></uv-button>
- </view>
-
- <!-- 文字生成图形 -->
- <canvas canvas-id="myCanvas" id="myCanvas" class="canvas"
- :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"></canvas>
-
- <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>
- </view>
- </view>
- </view>
-
- <!-- 分辨率 -->
- <uv-picker ref="pickerRatio" :columns="columnsRatio" @confirm="confirmRatio"></uv-picker>
-
- <!-- 颜色 -->
- <uv-popup ref="pickerColor" mode="bottom" round="10" :closeable="true">
- <view class="color_list">
- <view class="color_item" v-for="(item, index) in colorList" :class="{ hover: color === item }"
- :key="index" :style="{ backgroundColor: item }" @click="clickColor(item)"></view>
- </view>
- </uv-popup>
-
- </view>
-</template>
-
-<script>
- let ctx = null
- export default {
- data() {
- return {
- tempName: '',
- pic: '',
- specList: [{
- name: '12*24'
- }, {
- name: '16*24'
- }, {
- name: '18*24'
- }],
- specVal: '12*24',
- specWidth: 24,
- specHeight: 12,
- modeList: [{
- name: '单个动画帧',
- val: 0
- },
- {
- name: '动画帧切换',
- val: 1
- }
- ],
- modeVal: 0,
- columnsTotal: [
- [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
- 28, 29, 30
- ]
- ],
- total: 1, //动画总帧数
- columnsSpeed: [
- [10, 20, 30, 40, 50, 60, 70, 80, 90]
- ],
- speed: 10,
- cycle: 1,
- fpsData: [],
- fpsIndex: 1, //第几帧
- fpsIds: [],
-
- baseURL: '',
- fileList1: [],
- animationData: [],
- fpsTotal: 1,
-
-
-
- // 模版动画
- pixelData: [],
- canvasData: [],
- canvasWidth: 240,
- canvasHeight: 120,
- width: 24,
- height: 12,
- color: '#FF0000',
- gridRect: null, // 存储像素网格的位置信息
- lastCell: null, // 防止重复绘制
- columnsRatio: [
- ['12*24', '16*32', '24*48', '32*64', '40*80']
- ],
- // columnsRatio: [
- // ['12*24']
- // ],
- colorList: [
- '#FF0000',
- '#00FF00',
- '#0000FF',
- '#FFFF00',
- '#FFA500',
- '#800080',
- '#00FFFF',
- '#FFC0CB'
- ],
- fpsIndex: 0,
- fpsList: [],
- tId: '',
- tempInfo: {},
- titleName: '新增模版',
-
-
- }
- },
- onLoad(opt) {
- this.baseURL = uni.$uv.http.config.baseURL
- if (opt.index) {
- this.fpsIndex = opt.index
- console.log('fpsIndex--', this.fpsIndex)
- }
- },
- onShow() {
- this.pixelData = new Array(this.height * this.width).fill('')
- },
- onHide() {
-
- },
- onUnload() {
-
- },
- methods: {
- // 保存动画
- clickSaveData() {
- this.fpsList[this.fpsIndex] = this.pixelData;
- this.$refs.popupAnimation.close();
- },
- changeColor() {
- this.$refs.pickerColor.open();
- },
- clickColor(item) {
- console.log('confirmColor---', item);
- this.color = item;
- this.$refs.pickerColor.close();
- },
- changeRatio() {
- this.$refs.pickerRatio.open();
- },
- // 分辨率
- confirmRatio(e) {
- console.log('confirmRatio---', e.value[0]);
- const ratio = e.value[0].split('*');
- console.log('ratio---', ratio[0]);
- this.height = Number(ratio[0]);
- this.width = Number(ratio[1]);
- this.canvasWidth = this.width * 10;
- this.canvasHeight = this.height * 10;
- // 重新初始化一维数组
- this.pixelData = new Array(this.height * this.width).fill('')
- console.log('pixelData---', this.pixelData);
- },
- // 预先获取像素网格的位置信息
- getGridRect(cb) {
- if (this.gridRect) {
- cb(this.gridRect)
- } else {
- uni.createSelectorQuery().in(this).select('.pixel_grid').boundingClientRect(rect => {
- this.gridRect = rect
- cb(rect)
- }).exec()
- }
- },
- // 计算触摸点对应的格子
- getCellByTouch(touch, cb) {
- this.getGridRect(rect => {
- if (!rect) return
- const x = touch.clientX - rect.left
- const y = touch.clientY - rect.top
- const cellWidth = rect.width / this.width
- const cellHeight = rect.height / this.height
- const colIndex = Math.floor(x / cellWidth)
- const rowIndex = Math.floor(y / cellHeight)
- if (
- colIndex >= 0 && colIndex < this.width &&
- rowIndex >= 0 && rowIndex < this.height
- ) {
- cb({
- rowIndex,
- colIndex
- })
- }
- })
- },
- handleTouchStart(e) {
- // 阻止默认行为
- e.preventDefault && e.preventDefault();
-
- if (!e.touches.length) return
- this.getCellByTouch(e.touches[0], cell => {
- this.lastCell = cell
- this.handleClick(cell.rowIndex, cell.colIndex)
- })
- },
- handleTouchMove(e) {
- // 阻止默认行为
- e.preventDefault && e.preventDefault();
-
- if (!e.touches.length) return
- this.getCellByTouch(e.touches[0], cell => {
- // 只在格子变化时才触发
- if (!this.lastCell || this.lastCell.rowIndex !== cell.rowIndex || this.lastCell.colIndex !==
- cell.colIndex) {
- this.lastCell = cell
- this.handleClick(cell.rowIndex, cell.colIndex)
- }
- })
- },
- handleTouchEnd() {
- this.lastCell = null
- },
- handleClick(rowIndex, colIndex) {
- console.log(rowIndex, colIndex)
- // 计算一维数组的索引
- const index = rowIndex * this.width + colIndex
- // 如果当前像素有颜色,则清除;如果没有颜色,则添加当前选择的颜色
- const currentColor = this.pixelData[index]
- const newColor = currentColor ? '' : this.color
- this.$set(this.pixelData, index, newColor)
- },
- clear() {
- ctx && ctx.clearRect(0, 0, 1000, 1000)
- // 将所有像素点设置为空字符串
- this.pixelData = new Array(this.height * this.width).fill('')
- },
- // 模版动画弹出框
- changeAnimation(e) {
- //console.log('打开关闭模版动画弹窗触发---',e)
- // ios设备不支持弹出框横屏切换
- // if(e.show){
- // if (typeof plus !== 'undefined') {
- // plus.screen.lockOrientation('landscape-primary')
- // }
- // }
- // else{
- // if (typeof plus !== 'undefined') {
- // plus.screen.lockOrientation('portrait')
- // }
- // }
- },
- // 打开模版动画
- popupAnimationOpen(index) {
- this.fpsIndex = index
- if (this.fpsList[this.fpsIndex] == null) {
- console.log('第一次编辑-----null')
- this.pixelData = new Array(this.height * this.width).fill('')
- } else {
- console.log('已编辑过-----')
- this.pixelData = this.fpsList[this.fpsIndex]
- }
- },
- // 关闭模版动画
- popupAnimationClose() {
- this.$refs.popupAnimation.close();
- this.pixelData = new Array(this.height * this.width).fill('')
- },
- // 屏幕规格
- changeSpec(e) {
- const ratio = e.split('*');
- this.specHeight = Number(ratio[0]);
- this.specWidth = Number(ratio[1]);
- },
- // 偶数行数据反转
- toSnakeData(pixelData, cols = 24) {
- const rows = []
- for (let i = 0; i < pixelData.length; i += cols) {
- let row = pixelData.slice(i, i + cols)
- if ((rows.length + 1) % 2 === 0) {
- row.reverse() // 偶数行反转
- }
- rows.push(row)
- }
- return rows
- },
- // 二维数组转字符串
- mergeRowsToString(rows) {
- const rowStrings = rows.map(r => r.join('')) // 每行合并
- return rowStrings.join('') // 所有行合并
- },
- // 生成一帧动画其他帧数据
- generateFrames(data, frameCount = 12) {
- const cols = this.specWidth; // 每行 24 个
- const rows = this.specHeight; // 共 12 行
- const frames = [];
- // 拆分成 12 行
- 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 = [];
- // 每行左移 f 次
- 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;
- },
-
- clickSpeed() {
- this.$refs.pickerSpeed.open();
- },
- confirmSpeed(e) {
- console.log('confirmSpeed', e.value[0]);
- this.speed = e.value[0]
- },
- clickTotal() {
- if (this.total != 1) {
- this.$refs.pickerTotal.open();
- }
- },
- confirmTotal(e) {
- console.log('confirmTotal', e.value[0]);
- this.total = e.value[0]
- this.adjustFpsData()
- },
- changeMode(e) {
- this.fpsList = []
- this.fpsData = []
- if (e == 0) {
- this.total = 1
- } else {
- this.total = 2
- }
- this.adjustFpsData()
- },
- adjustFpsData() {
- // 如果 fpsData 太长 → 截取
- if (this.fpsList.length > this.total) {
- this.fpsList = this.fpsList.slice(0, this.total);
- }
- // 如果 fpsData 太短 → 补齐 null
- else if (this.fpsList.length < this.total) {
- const missing = this.total - this.fpsList.length;
- this.fpsList = this.fpsList.concat(new Array(missing).fill(null));
- }
-
- // 如果 fpsList 太长 → 截取
- if (this.fpsData.length > this.total) {
- this.fpsData = this.fpsData.slice(0, this.total);
- }
- // 如果 fpsList 太短 → 补齐 null
- else if (this.fpsData.length < this.total) {
- const missing = this.total - this.fpsData.length;
- this.fpsData = this.fpsData.concat(new Array(missing).fill(null));
- }
- },
- // 删除图片
- deletePic(event) {
- this[`fileList${event.name}`].splice(event.index, 1)
- },
- // 新增图片
- async afterRead(event) {
- console.log('event---', event)
- // 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
- let lists = [].concat(event.file)
- let fileListLen = this[`fileList${event.name}`].length
- lists.map((item) => {
- this[`fileList${event.name}`].push({
- ...item,
- status: 'uploading',
- message: '上传中'
- })
- })
- for (let i = 0; i < lists.length; i++) {
- const result = await this.uploadFilePromise(lists[i].url)
- this.avatar = result
- let item = this[`fileList${event.name}`][fileListLen]
- this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
- status: 'success',
- message: '',
- url: result
- }))
- fileListLen++
- }
- },
- uploadFilePromise(url) {
- return new Promise((resolve, reject) => {
- let a = uni.uploadFile({
- url: this.baseURL + '/common/uploadFileByAnny', // 仅为示例,非真实的接口地址
- filePath: url,
- name: 'file',
- success: (res) => {
- console.log('图片-----', JSON.parse(res.data))
- setTimeout(() => {
- resolve(JSON.parse(res.data).fileName)
- }, 1000)
- }
- });
- })
- },
- // 生成点阵数据
- generateDotMatrix(char) {
- return new Promise((resolve, reject) => {
-
- // 使用 uni-app 的 canvas context
- ctx = uni.createCanvasContext('myCanvas', this);
-
- // 设置字体样式
- ctx.setFontSize(this.canvasHeight * 0.9);
- ctx.setFillStyle('#000')
- ctx.setTextAlign('center')
- ctx.setTextBaseline('middle')
- // 绘制文字
- // 计算文字位置
- // 水平居中
- const x = this.canvasWidth / 2;
- // 垂直居中,稍微向上偏移一点
- const y = this.canvasHeight / 2;
-
- // 绘制文字
- ctx.fillText(char, x, y);
-
- // 绘制到画布
- ctx.draw(false, () => {
- // 获取画布数据
- uni.canvasGetImageData({
- canvasId: 'myCanvas',
- x: 0,
- y: 0,
- width: this.canvasWidth,
- height: this.canvasHeight,
- success: (res) => {
- console.log(res)
- const imageData = res.data;
- // 生成一维数组
- const pixelArray = new Array(this.height * this.width).fill('')
-
- // 计算每个格子的像素大小
- const cellWidth = this.canvasWidth / this.width;
- const cellHeight = this.canvasHeight / this.height;
-
- // 生成一维数组
- for (let y = 0; y < this.height; y++) {
- for (let x = 0; x < this.width; x++) {
- let blackPixels = 0;
- let totalPixels = 0;
-
- // 对每个格子进行采样
- for (let py = 0; py < cellHeight; py++) {
- for (let px = 0; px < cellWidth; px++) {
- const pixelY = Math.floor(y * cellHeight + py);
- const pixelX = Math.floor(x * cellWidth + px);
-
- const idx = (pixelY * this.canvasWidth +
- pixelX) * 4;
- const r = imageData[idx];
- const g = imageData[idx + 1];
- const b = imageData[idx + 2];
- const a = imageData[idx + 3];
-
- // 计算亮度
- const brightness = 0.3 * r + 0.59 * g + 0.11 *
- b;
-
- // 如果亮度小于阈值且透明度大于阈值,则认为是黑色
- if (brightness < 128 && a > 128) {
- blackPixels++;
- }
- totalPixels++;
- }
- }
-
- // 如果黑色像素占比超过阈值,则设置为当前选择的颜色
- const isBlack = blackPixels / totalPixels > 0.3;
- const arrayIndex = y * this.width + x;
- pixelArray[arrayIndex] = isBlack ? this.color :
- ''; // 改为颜色值或空字符串
- }
- }
-
- resolve(pixelArray);
- },
- fail: (err) => {
- reject(err);
- }
- });
- });
- });
- },
-
- // 修改添加文字方法
- async addText() {
- uni.showModal({
- title: '添加文字',
- content: '',
- editable: true,
- placeholderText: '请输入文字',
- success: async (res) => {
- if (res.confirm) {
- const text = res.content;
- if (text.length > 0) {
- try {
- // 生成点阵数据
- const dotMatrix = await this.generateDotMatrix(text);
- console.log('点阵数据:', dotMatrix);
- this.pixelData = dotMatrix
- } catch (error) {
- console.error('生成点阵失败:', error);
- uni.showToast({
- title: '生成点阵失败',
- icon: 'none'
- });
- }
- } else {
- uni.showToast({
- title: '请输入文字',
- icon: 'none'
- });
- }
- }
- }
- });
- },
- }
- }
-</script>
-
-<style lang="scss" scoped>
-.color_list {
- width: 100%;
- display: flex;
- justify-content: center;
- flex-wrap: wrap;
- gap: 30rpx;
- box-sizing: border-box;
- padding: 50rpx;
-
- .color_item {
- width: 30rpx;
- height: 30rpx;
- border-radius: 5rpx;
- }
-}
-
-.controls {
- position: absolute;
- bottom: 10px;
- left: 50px;
- display: flex;
- gap: 20px;
- z-index: 9;
-}
-
-.animation_pixel {
- width: 100vw;
- height: 100vh;
- position: relative;
-
- .back_btn {
- position: absolute;
- left: 30px;
- top: 20px;
- z-index: 9999;
- }
-
- .canvas {
- margin: 100rpx auto 0;
- background: #ccc;
- position: absolute;
- z-index: 1;
- left: -9999px;
- top: -9999px;
- }
-
- .pixel_grid {
- position: fixed;
- left: 0;
- top: var(--status-bar-height);
- right: 0;
- bottom: 0;
- display: grid;
- gap: 1px;
- box-sizing: border-box;
- background: #eee;
- touch-action: none;
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- user-select: none;
-
- .pixel_cell {
- display: flex;
- align-items: center;
- justify-content: center;
- box-sizing: border-box;
- background: #fff;
-
- .pixel_cell_item {
- width: 50%;
- height: 50%;
- aspect-ratio: 1/1;
- font-size: 10rpx;
- }
- }
- }
-
-}
-</style>
\ No newline at end of file
--
Gitblit v1.10.0