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

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

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

例如项目名称为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()方法,可以轻松实现预览和保存,分享图片

运行测试

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

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

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

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

十字绣-龙猫

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用object.defineProperty来更新数据示例

Object.defineProperty() 方法会直接在一个对象上定义一个新属性&#xff0c;或者修改一个对象的现有属性&#xff0c;并返回此对象。 Object.defineProperty&#xff08;&#xff09;可以为对象的属性添加特性&#xff0c;每一个被添加过的属性&#xff0c;都会拥有属于自己的…

【C++初阶】C++——模板初阶与泛型编程

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;C初阶 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 1. 泛型编程2. 函数模板…

C Primer Plus第四章编程练习答案

学完C语言之后&#xff0c;我就去阅读《C Primer Plus》这本经典的C语言书籍&#xff0c;对每一章的编程练习题都做了相关的解答&#xff0c;仅仅代表着我个人的解答思路&#xff0c;如有错误&#xff0c;请各位大佬帮忙点出&#xff01; 1.编写一个程序&#xff0c;提示用户输…

自学网络安全最细规划(建议收藏)

01 什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防两面…

自古以来,反射也是兵家必争之地

成文耗时1小时&#xff0c;阅读5min&#xff0c;有用指数5颗星。 这几天收到一个战术性需求&#xff0c;将一大坨字段序列化为特定格式的字符串。 大概是下表&#xff1a; 序号字段名描述是否必填0logVersion日志版本是1productName产品是2serviceName服务是.........25extend3…

8项seo的日常工作

SEO的日常工作涵盖了一系列任务和活动&#xff0c;旨在优化网站以提高在搜索引擎中的排名和可见性。 以下是SEO的日常工作内容&#xff1a; 关键词研究和优化&#xff1a;定期进行关键词研究&#xff0c;寻找与目标受众和业务相关的热门关键词。优化网站内容、标题、元描述和链…

这些脑洞大开的论文标题,也太有创意了O(∩_∩)O

microRNAs啊microRNAs&#xff0c;谁是世界上最致命的髓母细胞瘤microRNAs&#xff1f; 这个标题很容易让人联想到白雪公主后妈说的那句话&#xff1a;Mirror mirror on the wall, who is the fairest of them all? 02 一氧化碳&#xff1a;勇踏NO未至之境 NO 指 nitric oxide…

合并两个有序链表(java)

leetcode 21题&#xff1a;合并两个有序链表 题目描述解题思路&#xff1a;链表的其它题型。 题目描述 leetcode21题&#xff1a;合并两个有序链表 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例&#xff1a; 输入&…

MySQL 数值函数

文章目录 数值函数1. abs(num)2. ceil(num)3. floor(num)4. mod(num1,num2)5. rand()6. round(num,n)7. truncate(num,n)8. sqrt(num) 数值函数 数值函数用来处理数值方面的运算&#xff0c;能够提高用户的工作效率。常用的数值函数如下表所示&#xff0c;函数括号内为输入的参…

四足机器人A1目标跟踪

四足机器人A1目标跟踪 前期准备工作1.安装TeamViewer2.将四足机器人所有线连接好3.将四足机器人调至运动模式 运行流程1.开机阶段2.运行阶段 效果展示代码配置 前期准备工作 1.安装TeamViewer 由于外接屏幕损坏&#xff0c;故四足机器人内部配置了TeamViewer&#xff0c;因此…

【Linux】线程同步

文章目录 条件变量相关函数初始化条件变量-pthread_cond_init销毁条件变量-pthread_cond_destroy等待条件变量-pthread_cond_wait唤醒等待条件变量pthread_cond_broadcastpthread_cond_signal 小例子关于等待函数的补充条件变量使用规范 条件变量相关函数 初始化条件变量-pthr…

如何让自动化测试框架更自动化?

一、引言 ​对于大厂的同学来说&#xff0c;接口自动化是个老生常谈的话题了&#xff0c;毕竟每年的MTSC大会议题都已经能佐证了&#xff0c;不是大数据测试&#xff0c;就是AI测试等等&#xff08;越来越高大上了&#xff09;。不可否认这些专项的方向是质量智能化发展的方向&…

IMX6ULL裸机篇之IIC协议

一. IIC实验简介 I2C 是最常用的通信接口&#xff0c;众多的传感器都会提供 I2C 接口来和主控相连。 比如摄像头、 加速度计、触摸屏等。 I.MX6U-ALPHA开发板 使用 I2C1 接口连接了一个距离传感器 AP3216C &#xff0c;本章我们就来学习如何使用 I.MX6U 的 I2C 接口…

【JavaSE】Java基础语法(十):构造方法

文章目录 ⛄1. 构造方法的格式和执行时机⛄2. 构造方法的作用⛄3. 构造方法的特点⛄4. 构造方法的注意事项⛄5. 构造方法为什么不能被重写 在面向对象编程的思想中&#xff0c;构造方法&#xff08;Constructor&#xff09;是一个特殊的函数&#xff0c;用于创建和初始化类的对…

华为OD机试之模拟商场优惠打折(Java源码)

模拟商场优惠打折 题目描述 模拟商场优惠打折&#xff0c;有三种优惠券可以用&#xff0c;满减券、打折券和无门槛券。 满减券&#xff1a;满100减10&#xff0c;满200减20&#xff0c;满300减30&#xff0c;满400减40&#xff0c;以此类推不限制使用&#xff1b; 打折券&…

GoWeb -- gin框架的入门和使用

认识gin go流行的web框架 go从诞生之初就带有浓重的开源属性&#xff0c;其原生库已经很强大&#xff0c;即使不依赖框架&#xff0c;也能进行高性能开发&#xff0c;又因为其语言并没有一定的设计标准&#xff0c;所以较为灵活&#xff0c;也就诞生了众多的框架&#xff0c;各…

视频怎么加水印?如何录制带水印的视频?

案例&#xff1a;如何给视频添加水印&#xff1f; 【我发布在短视频平台的视频&#xff0c;总是被别人盗用&#xff0c;我想给自己的视频添加水印。有没有视频添加水印的方法&#xff1f;在线等&#xff01;】 很多视频制作者或者爱好者&#xff0c;都希望自己的视频作品得到…

腾讯云轻量服务器镜像安装宝塔Linux面板怎么使用?

腾讯云轻量应用服务器宝塔面板怎么用&#xff1f;轻量应用服务器如何安装宝塔面板&#xff1f;在镜像中选择宝塔Linux面板腾讯云专享版&#xff0c;在轻量服务器防火墙中开启8888端口号&#xff0c;然后远程连接到轻量服务器执行宝塔面板账号密码查询命令&#xff0c;最后登录和…

【P31】JMeter 循环控制器(Loop Controller)

这文章目录 一、循环控制器&#xff08;Loop Controller&#xff09;参数说明二、测试计划设计2.1、设置循环次数2.2、勾选永远2.3、设置线程组的持续时间 一、循环控制器&#xff08;Loop Controller&#xff09;参数说明 可以对部分逻辑按常量进行循环迭代 选择线程组右键 …

Lua学习笔记:C/C++和Lua的相互调用

前言 本篇在讲什么 C/C和Lua的相互调用 本篇适合什么 适合初学Lua的小白 适合需要C/C和lua结合开发的人 本篇需要什么 对Lua语法有简单认知 对C/C语法有简单认知 依赖Lua5.1的环境 依赖VS 2017编辑器 本篇的特色 具有全流程的图文教学 重实践&#xff0c;轻理论&…