【十字绣】传统手艺-微信小程序开发流程详解

news2024/11/13 15:57:17

还记得小时候看过母亲的十字绣吗,易学易懂,就是用专用的绣线和十字格布,通过平面坐标计找出位置,对照专用的图案进行刺绣,可作出心中所想的画,奈何所需材料成本不小,这里用小程序简单模拟十字绣,喜欢的话可用它练习一下。

打开微信开发工具,选择小程序,创建一个项目,
新建小程序

例如项目名称为miniprogram-cross-sitich,然后选择以下,再确定创建

  • AppID 使用测试号
  • 不使用云服务
  • JavaScript - 基础模板

游戏页面

小程序创建好后,找到文件pages/index/index.js,这是第一个页面的逻辑文件,

找到里面的方法onLoad(),在里面添加如下代码,可自动进到游戏页面

wx.navigateTo({
  url: '/pages/game/game',
})

页面布局

创建一个游戏页面,文件在pages/game/game.wxml

页面布局参照HTML网页的vue组件化布局,做好的页面运行显示效果图如下
在这里插入图片描述

布局中用到了一个canvas和用作背景显示的image组件,
还有颜色盒,可以左右拖动选择更多颜色,
还有底部的四个按钮,可清空,缩放网格,预览效果

游戏逻辑

接下来,在文件pages/game/game.wxml中写代码,实现游戏逻辑

初始化

实现的前提是做好初始化处理,把需要用到的全局变量,设置到data中,以便后面能读取和重新赋值

data:{
	bgImg:'',
    colorList:['F08784','EB3324','774342','8E403A','3A0603','9FFCFD','73FBFD','3282F6','0023F5','00129A','16417C','000C7B','FFFE91','FFFD55','F09B59','F08650','784315','817F26','7E84F7','732BF5','3580BB','00023D','58135E','3A083E','A1FB8E','A1FA4F','75F94D','75FA61','75FA8D','818049','EF88BE','EE8AF8','EA3FF7','EA3680','7F82BB','75163F','377D22','377E47','367E7F','507F80','183E0C','173F3F','741B7C','39107B','000000','808080','C0C0C0','FFFFFF'].map(color=>'#'+color),
    scale:1.0,
    colorCurrent:0
}
  • colorList就是颜色盒的数组数据,可选择的颜色都在这里定义;
  • colorCurrent指定数组中的其中的一个颜色值,选定笔色;
  • scale就是画布中的初始缩放尺寸,值1.0是表示原始大小;

获取画布

当页面加载完成时,会调用其中的onReady()事件,

在这里写代码,获取到布局文件中的canvas组件

wx.createSelectorQuery().select('#canv').fields({
  size:true, node:true
},res=>{
  //...省略了
  const ctx = res.node.getContext('2d');
  this.canvasData = {
    ctx,
    canvas:res.node,
    width:ctx.canvas.width,
    height:ctx.canvas.height
  };
  this.initCanvas();
}).exec()

传入的ctx就是画布的context对象,可用来绘制,
调用的initCanvas()是初始化方法,这里将继续处理,绘制网格

绘制网格

方法initCanvas()是把所有网格数据建出来,用灰白色区分不同位置的格子,

类似于PS软件中的透明背景图,代码如下

// 获取屏幕宽度和像素比
const { windowWidth, pixelRatio } = wx.getSystemInfoSync();
// 获取画布宽高
const { width, height } = this.canvasData;
// 算出格子大小,列和行
const size = Math.floor(windowWidth/pixelRatio/16);
const cols = Math.floor(width/size);
const rows = Math.floor(height/size);
// 算出内边距
const paddingH = (width-cols*size)*0.5;
const paddingV = (height-rows*size)*0.5;
// 定义网格数组
const grids = [];
for(let r=0; r<rows; r++){
  for(let c=0; c<cols; c++){
    let g = {
      left: c*size+paddingH,
      top: r*size+paddingV,
      //...省略了
    };
    // 将一个格子数据添加到数组中
    grids.push(g);
  }
}
// 设置网格数据,将上面定义的数据暂存起来
this.gridsData = {
  grids,
  //...省略了
  positon: {
    //...画布位置
  }
};
this.redrawGrils();

其中redrawGrils()就是绘制所有网格的方法,用canvas组件的context对象去绘制grids

实现操作

网格画出来后,接下来处理用户的操作

触摸事件

当用户手指触摸到页面布局中画布canvas区域时,画布会调用三个不同的事件,

分别是触摸的开始,移动,结束事件,分别做一下处理,

触摸开始事件

会调用onTouchStart(e)方法,代码如下

const touch = e.touches[0];
//记录触摸开始时的点
this.downPositon = touch;
//重置清除 触摸结束时的点
this.upPosition = undefined;
触摸移动事件

调用onTouchMove(e)方法,代码如下

const touch = e.touches[0];
const { downPositon } = this;
const { scale } = this.data;
this.upPosition = touch;
// 计算触摸结束到开始时的相对位置
let offsetPositon = {
  left: touch.x - downPositon.x,
  top: touch.y - downPositon.y
};
// 重新绘制所有网格
this.redrawGrils(scale, offsetPositon);

触摸移动时,就可以拖动画布,
调用方法redrawGrils(scale, offsetPositon),传入的第二个参数可以改变画布的位置,然后重新绘制网格和涂点位置

触摸结束事件

调用方法onTouchEnd(),代码如下

const { downPositon, upPosition } = this;
// 当触摸结束时,判断结束点可区分是否移动过
if (upPosition) {
  //省略了...
  let offsetPositon = {
	//省略了...
  };
  //如果移动了,就更新一下拖动画布后的位置
  this.gridsData.positon.left+=offsetPositon.left;
  this.gridsData.positon.top+=offsetPositon.top;
  return;
}

// 执行到这里时,以下就是对点击操作处理
const { grids, size } = this.gridsData;
const { scale, colorList, colorCurrent } = this.data;
// 触摸按下时的开始点
let touch = downPositon;
// 根据缩放和位置,找出格子
let s = size*scale;
// 偏移一半格子的距离
let offset = s*0.5;
let left = touch.x-offset;
let top = touch.y-offset;
let grid = grids.find(grid=>grid.left<=left && grid.left+s>left && grid.top<=top && grid.top+s>top);
//判断是否碰到格子,没有就返回
if(grid==undefined) return;
//省略了...
//判断是否涂点过(刺绣)
if(grid.flag) {
	//擦除涂点
  grid.flag = false;
  this.drawFlag(grid);
}else{
	//涂点
  grid.color = colorList[colorCurrent];
  grid.flag = true;
  this.drawFlag(grid);
}

其中drawFlag(grid)就是绘制格子方法,绘制交叉的涂点

选择颜色

这里实现选择颜色盒的颜色,很简单,

选择其中一个颜色格子时,会调用方法onClickColor(e),代码如下

const { index } = e.currentTarget.dataset;
// 更新指向颜色数组中的索引(游标)
this.setData({
  colorCurrent: index
})

放大和缩小

由于是在手机屏幕上操作,当显示的网格过小,手指会点不准的,

需要点击放大按钮,放大网格,这样就点得准,方便涂点,

点击按钮会调用方法onClickKey(e),代码如下

switch(e.currentTarget.dataset.key){
  case 'preview':// 预览
    this.saveImage();
    break;
  case 'amplify':// 放大
    this.amplifyGrids();
    break;
  case 'reduce':// 缩小
    this.reduceGrids();
    break;
  case 'clear':// 清空
    wx.showModal({
      title: '系统提示',
      content: '确定要清空吗?',
      complete: (res) => {
    	//...
        if (res.confirm) {
          this.resetGridData()
        }
      }
    });
    break;
}

页面中的底部四个按钮都是调用方法onClickKey(e),传入不同的参数,
例如,点击放大按钮时,调用方法传入参数amplify
再选择调用方法amplifyGrids()进行放大处理

放大的方法,就是调用了redrawGrils(scale),实现缩放,代码如下

let { scale } = this.data;
//放大
scale+=0.1;
//省略了...
//重新绘制网格,传入缩放比例
this.redrawGrils(scale);

有没有看出来,很多地方都调用了方法redrawGrils(scale)重绘网格,
这个方法实现了缩放和平移,是稍微复杂一点,不到60行代码,不多吧

缩小的方法,调用reduceGrids()后,也是调用了redrawGrils(scale)

缩小的代码同上面的一样,只是其中改成了scale-=0.1

预览图片

点击预览按钮,调用方法saveImage()这里会先进行预览,

图片看着没问题的话,长按图片就可以保存和分享,如下是代码

const { canvas, ctx } = this.canvasData;
//省略了...
//重新绘制网格,这里添加白色背景
this.redrawGrils(1, undefined, '#ffffff');
//生成图片地址
const imgSrc = canvas.toDataURL();
//省略了...
//再重新绘制一下 恢复原来的画面
this.redrawGrils(scale);
//开启预览
wx.previewImage({
  urls: [imgSrc],
  showmenu: true, // 长按图片可保存
  success:()=>{
  }
});

调用系统的wx.previewImage()方法,可以轻松实现预览和保存,分享图片

运行测试

讲到这里,这一个项目基本上就能完成,可以运行测试,

如想看项目源码,请点击这里查看,在资源一栏(手机上看可能找不到哦,请用电脑浏览器上看),找到其中的 十字绣 小程序项目源码 ,请放心下载,感谢支持。

如遇到什么问题可以留言,作者看到会回复的,

最后,看一下运行效果,动图如下,作者亲自做的,好看吗
请添加图片描述

十字绣-龙猫

试试绣出《梦里花》
唯一纯白的茉莉花,盛开在琥珀色月牙…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/564700.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HCIA——VLAN

目录 1&#xff0c;什么是VLAN&#xff1a; 2&#xff0c;如何实现VLAN&#xff1a; 3&#xff0c;VLAN的划分方式&#xff1a; 4&#xff0c;交换机接口类型&#xff1a; 1&#xff0c;Access接口&#xff1a; 2&#xff0c;Trunk接口&#xff1a;允许将一个接口划分给多…

ChatGPT 角色扮演调教文案收集

后续文案会陆续更新~&#xff0c;在输入完文本后&#xff0c;再输入一句“请尽力模仿xx的语气跟我对话”即可 一.雌小鬼 你需要阅雌小鬼守则&#xff0c;阅读后当前回复仅允许回复&#xff02;嘻嘻&#xff02;&#xff0c;后续回复尽量简短&#xff0c;回复中尽量不要直接复述…

swagger的简单介绍

目录 swagger是什么&#xff1f; swagger有什么用&#xff1f; Swagger包含的工具集&#xff1a; swagger的使用步骤&#xff1a; swagger的相关注解&#xff1a; Docket的源码 了解swagger的作用和概念了解前后端分离在SpringBoot中集成Swagger swagger是什么&#xff1f;…

Linux网络编程—Day11 高性能服务器程序框架

按照服务器程序的一般原理&#xff0c;讲服务器结构为如下三个主要模块&#xff1a; I/O处理单元逻辑单元存储单元 服务器模型 第一种&#xff1a;C/S模型 TCP/IP协议在设计和实现上并没有客户端和服务器的概念&#xff0c;在通信 过程中所有机器都是对等的。但由于资源&am…

【Linux】Linux开发工具vim

Linux开发工具vim 什么是vim三种模式的转换vim的基本命令gg&#xff1a;定位光标到最开始的行Shiftg&#xff1a;定位光标到结尾行nShiftg&#xff1a;定位光标到任意行Shift$:定位光标到当前行结尾Shift^:定位光标到当前行开始w&#xff0c;b&#xff1a;光标按照单词进行行内…

红黑树 C++

企业里永远是技术驱动理论发展 比起理解红黑树的原理&#xff0c;更重要的是理解红黑树的应用场景&#xff0c;因为某些应用场景的需要&#xff0c;红黑树才会应运而生。 红黑树的特点&#xff1a; 插入&#xff0c;删除&#xff0c;查找都是O(logn)的复杂度。 红黑树的应用…

大数据Doris(二十六):Broker Load基本原理和语法介绍

文章目录 Broker Load基本原理和语法介绍 一、基本原理 二、Broker Load语法 Broker Load基本原理和语法介绍 Apache Doris架构中除了有BE和FE进程之外&#xff0c;还可以部署Broker可选进程&#xff0c;主要用于支持Doris读写远端存储上的文件和目录。例如&#xff1a;Apa…

spring boot +Sa-Token优雅的实现项目鉴权!

1. 技术选型 最近在做登录、授权的功能&#xff0c;一开始考虑到的是spring boot spring security&#xff0c;但spring security太重&#xff0c;而我们是轻量级的项目&#xff0c;所以&#xff0c;spring security不适合我们。 而后考虑spring boot shiro&#xff0c;但s…

【老王读SpringMVC-5】Controller method 是如何执行的?

通过前面对 Controller method 参数绑定的分析&#xff0c;我们知道&#xff0c; 被 RequestMapping 标记 handler method 的执行是通过调用 RequestMappingHandlerAdapter#handle()。 RequestMappingHandlerAdapter#handle() 具体的调用过程如下&#xff1a; 参数解析、han…

【Java基础篇】运算符

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a;Java.SE&#xff0c;本专栏主要讲解运算符&#xff0c;程序逻辑控制&#xff0c;方法的使用&…

由浅入深Dubbo网络通信深入解析

目录 1 dubbo中数据格式2 消费方发送请求3 提供方接收请求4 提供方返回调用结果5 消费方接收调用结果6 异步转同步7 异步多线程数据一致8 心跳检查 1 dubbo中数据格式 解决socket中数据粘包拆包问题&#xff0c;一般有三种方式 定长协议&#xff08;数据包长度一致&#xff09…

5GNR——RACH随机接入流程(1):随机接入的原因

1、随机接入触发原因 1- Initial access from RRC_IDLE; 2- RRC Connection Re-establishment procedure; 3- DL or UL data arrival during RRC_CONNECTED when UL synchronisation status is “non-synchronised”; 4- UL data arrival during RRC_CONNECTED when there are …

Java之运算符

&#xff0b;加号的作用 1.表示正数 2.相加运算符 3.进行字符串的拼接 4.自增 Tips&#xff1a; 运算运算符优于 扩展赋值运算符 byte a ; int b ; ab&#xff1b; 右侧为byte&#xff0c;无需强制转换 aab; 右侧为int&#xff0c;需强制转换为byte&#xff0c;赋给左边…

解码区块链:探索去中心化世界的奥秘与潜力

&#x1f41f; 区块链技术的基本原理&#x1f41f; 区块链技术的应用场景&#x1f41f; 区块链技术的挑战与前景 区块链技术作为一项创新性的技术&#xff0c;引领着数字时代的变革。它以其去中心化、透明性和安全性的特点&#xff0c;为各行业带来了无限可能。在本篇博客中&am…

《程序员面试金典(第6版)》面试题 02.05. 链表求和(构建一个新链表)

题目解析 给定两个用链表表示的整数&#xff0c;每个节点包含一个数位。这些数位是反向存放的&#xff0c;也就是个位排在链表首部。编写函数对这两个整数求和&#xff0c;并用链表形式返回结果。 题目传送门&#xff1a;面试题 02.05. 链表求和 示例&#xff1a; 输入&#x…

漏洞管理基础知识

漏洞管理对于端点安全至关重要&#xff0c;是在安全漏洞导致漏洞之前清除安全漏洞的最主动方法之一。 什么是漏洞 漏洞是软件中的错误代码段&#xff0c;会导致软件崩溃或以程序员从未预料到的方式做出响应。黑客可以利用漏洞对计算机系统进行未经授权的访问或对计算机系统执行…

第五十天学习记录:C语言进阶:位段

位段 什么是位段 位段的声明和结构是类似的&#xff0c;有两个不同&#xff1a; 1、位段的成员可以是int,unsigned int或signed int。 2、位段的成员名后边有一个冒号和一个数字。 #define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>//位段-二进制位 struct A {int …

用脚本采集ChatGPT免翻免费镜像

新建了一个网站 ChatGPT人工智能中文站 - ChatGPT人工智能中文站 每天给大家更新可用的国内可用chatGPT免费镜像站 昨天发布了一个教程 本地安装 ChatGPT&#xff01;无需API、 免翻墙、完全免费使用纯正OpenAI的全部功能&#xff01; 支持 Windows、 Mac、NAS、Linux系统 …

led钨丝灯项目笔记

基于ESP-12E的LED钨丝灯作品 原理图&#xff1a; PCB&#xff1a; 嘉立创上面有些封装没有&#xff0c;需要自己画 画完这两个&#xff0c;此时它们还没有相关联&#xff0c;需要将它们关联起来 在封装管理器中将它们关联起来 在这里面就可以找到自己画的封装 如&#xff1a;…

MySQL数据库从入门到精通学习第5天(创建数据表,查看,修改表结构,删除表)

创建数据表&#xff0c;查看&#xff0c;修改表结构 创建数据表查看表结构修改表结构删除表 创建数据表 在对MySQL数据表进行操作之前我们需要创建数据库&#xff0c;并使用USE语句选择数据库。 创建数据库使用CREATE TABLE语句&#xff1a; 语法&#xff1a;CREATE [TEMPOR…