js实现动漫拼图1.0版

文章目录

  • 1 实现效果视频
  • 2 功能实现思路
  • 3代码实现

1 实现效果视频

在这里插入图片描述

拼图1.0

2 功能实现思路

布局忽略(小白学前端,不献丑了)

左侧拼图格
左侧4*4的拼图小格子

利用表格实现,规划好td的大小,给每个格子加上背景图片(将完整的图片裁剪为16张,命名规则为数字.png(1-16),利用二维数组存放四行四列的值从1-16,在遍历数组时,给他动态加上背景图片即可)item就是二维数组存的值,动态拼接上;photo是文件夹的名字images_1(或2,3,4)实现动态切换拼图图片
在这里插入图片描述

  let print = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]]
function updateUI() {print.forEach((subArr, i) => {subArr.forEach((item, j) => {tds[i * 4 + j].style.backgroundImage = "url(./images/" + photo + "/" + item +".png)";if (isStart && i == x0 && j == y0) {tds[i * 4 + j].style.border = "3px solid red";} else {tds[i * 4 + j].style.border = "";}})})

更换拼图图片

先获取元素,给其绑定单击事件。产生随机[1-4]的索引值(我当前有image_x文件四个,命名从_1到_4所以我需要的索引是1-4),获取当前展示的拼图的_x,确保产生的随机值与上次不同,不同之后就动态拼接新的src,在调用上面实现的ui函数,实现切换背景。

changeBtn.onclick = function () {isStart = false;print = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]]step=0;let index = parseInt(Math.random() * 4) + 1;let photo_id = look.src.split('/')[look.src.split('/').length - 2].split('_')[1];while (index == photo_id) {index = parseInt(Math.random() * 4) + 1;}photo = 'images_' + index;look.src = "./images/" + photo + "/canzhaotu.png";updateUI();}

重置功能(开始游戏或者打乱拼图)
随机交换打乱顺序(1.0版)

随机索引,实现随机的交换,调用自实现ui函数动态拼接,达到背景的更换,实现打乱拼图效果。
获取对应元素,绑定点击事件,点击调用之后,完成相应的初始化,此时给求助按钮绑定点击事件,开始播放背景音乐。

let restBtn = document.getElementById('rest');restBtn.onclick = function () {step = 0;isStart = true;shuffle();updateUI();initEvent();helpBtn.onclick = helpBake;bgMusic.play();}
// 打乱图片function shuffle() {print.forEach((subArr, i) => {subArr.forEach((item, j) => {let x = parseInt(Math.random() * 4);let y = parseInt(Math.random() * 4);let temp = print[i][j];print[i][j] = print[x][y];print[x][y] = temp;})})}

initEvent函数(给每一个td加点击事件)
1.0版本玩法不同,可以任意更换原始块(想要控制移动的块)

给每一个td也帮上点击事件,在点击时计算出对应的二维数组的i(行)和j(列)。tds是一维数组索引值从0-16,print二维数组,行索引0-3,列索引0-3。可以根据二维行列推出对应的移位tds中的索引位置,也可以反过来。
例如:
如果 是二维的第三行第三列(对应的行列索引是2,2),此时对应的td索引应该是10=2×4+2;index=i*(每一行的长度)+j;反过来就是i=向下取整[index/每行的长度],j=[index%每行的长度]

// 选择初始化位置function initEvent() {tds.forEach((td, index) => {td.onclick = function () {let x = parseInt(index / 4);let y = parseInt(index % 4);x0 = x;y0 = y;updateUI();}})}

给移动按钮加点击事件
当然,也加了键盘监听事件,监听上下左右键

 leftBtn.onclick = function () {if (isStart) {direction = 'left';move(direction);}}rightBtn.onclick = function () {if (isStart) {direction = 'right';move(direction);}}upBtn.onclick = function () {if (isStart) {direction = 'up';move(direction);}}downBtn.onclick = function () {if (isStart) {direction = 'down';move(direction);}}window.onkeyup = function (e) {if (isStart) {if (e.keyCode == 37) {direction = 'left';move(direction);} else if (e.keyCode == 38) {direction = 'up';move(direction);} else if (e.keyCode == 39) {direction = 'right';move(direction);} else if (e.keyCode == 40) {direction = 'down';move(direction);}}}

move函数

定义一个direction全局变量,记录方向,判断向那个方向移动,在边界位置时需要注意越界,下一步如果是越界的情况,就直接return,不进行交换了(也就是不移动),也不执行step++(此时不算步数)。step记录移动步数,而一直出现的x0,y0是为了记录原始块(正在操控移动的块,在updateUi中给其加个边框,与其他做区分)
在这里插入图片描述

 // 移动交换图片function move(direction) {let x, y;if (direction == 'left') {if (y0 - 1 < 0) {console.log("左边到边界了");return;} else {x = x0;y = y0 - 1;}} else if (direction == 'right') {if (y0 + 1 > 3) {console.log("右边到边界了");return;} else {x = x0;y = y0 + 1;}} else if (direction == 'up') {if (x0 - 1 < 0) {console.log("上边到边界了");return;} else {x = x0 - 1;y = y0;}} else if (direction == 'down') {if (x0 + 1 > 3) {console.log("下边到边界了");return;} else {x = x0 + 1;y = y0;}}step++;let temp = print[x][y];print[x][y] = print[x0][y0];print[x0][y0] = temp;// 更新坐标位置x0 = x;y0 = y;updateUI();}

判断输赢

同时定义一个victory二维数组,记录下正确顺序,当移动到最后print数组的元素重新排序正确时,就游戏结束了。

let print = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]]
let victory = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]];
// 判断是否胜利function judgeVictory() {for (let i = 0; i < 4; i++) {for (let j = 0; j < 4; j++) {if (print[i][j] != victory[i][j]) {return false;}}}return true;}

重点(难点)

求助按钮(实现提示)

思路:首先我们知道二维数组正确的顺序是1-16按序的,在打乱之后(其实就是这些值被打乱了),我们要还原,【在这个版本里是可以任意更换原始块的(我们移动操作的块)】,所以应该遍历这个print二维数组。去挨个检查里面存放的值的顺序,第一个位置的值应该是1,不是1就是需要被还原,那么再去遍历找寻那个块上的值是1,找到之后,让其和第一个位置的块实现交换,但是不能直接交换,那样就是显得很突兀,直接翻山越海了,应该让其有依据的一步一步归位。那就是找到num,根据num在找到对应的块的i和j,将i,j分别赋值给x0和y0,调用ui实现找到应该归位的那个原始块。
在这里插入图片描述

这个时候就用到了print二维数组的行列,利用destX记录绿色块的行坐标(i),利用destY记录绿色块的列坐标(j),用x0和y0记录原始块(红色块)的行和列坐标 (规定先横向移动,在纵向移动,这样就不会影响前面已经正确归位的块【当前num值之前的块】)

比较红色块和绿色块的横坐标(若y0<destY),说明红色块在绿色快的左边,那么应该右移;
比较红色块和绿色块的横坐标(若y0>destY),说明红色块在绿色快的右边,那么应该左移;
当y0=destY时,说明二者在同一列上,此时开始上移或下移(同理)
比较红色块和绿色块的纵坐标(若x0<destX),说明红色块在绿色快的上边,那么应该下移;
比较红色块和绿色块的纵坐标(若x0>destX),说明红色块在绿色快的下边,那么应该上移;
最后,当x0=destX时,说明这个红色块已经移动到了正确位置(绿色块的地方)。
清楚定时器(用定时器实现的循环,for循环太快,体现不出归位的过程)
在这里插入图片描述

 // 求助按钮function helpBake() {let destX;let destY;let num;//遍历寻找那个块的位置不对,找到不对的那个,返回这个位置正确的值应该是那个numwc: for (let i = 0; i < print.length; i++) {for (let j = 0; j < print[i].length; j++) {let index = i * 4 + j;if (print[i][j] != index + 1) {destX = i;destY = j;num = index + 1;break wc;}}}let bakeX;let bakeY;//遍历数组,去看当前那个块身上的值是numwc: for (let i = 0; i < print.length; i++) {for (let j = 0; j < print[i].length; j++) {if (print[i][j] == num) {bakeX = i;bakeY = j;break wc;}}}x0 = bakeX;y0 = bakeY;updateUI();// 开启自动巡航// 先横向移动,在纵向移动(必须得这样,不然就是bug)helpBtn.onclick = 'none';let timeId = setInterval(() => {// 先比较y0和destYif (y0 < destY) {direction = "right";move(direction);} else if (y0 > destY) {direction = "left";move(direction);} else {if (x0 > destX) {direction = "up";move(direction);} else if (x0 < destX) {direction = "down";move(direction);} else {// 归位了clearInterval(timeId);if (isStart) {helpBtn.onclick = helpBake;}}}}, 800)}

剩余的就是各种测试,找bug,改逻辑。

3代码实现

代码下载:https://www.alipan.com/s/WrkusEaP8Uq

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>body {background-image: url('./images/background.png');}.first {text-align: center;margin-top: 20px;margin-bottom: 20px;}td {width: 100px;height: 100px;background-image: url('./images/1.png');background-size: 100% 100%;background-repeat: no-repeat;}.second {width: 60%;margin: 0 auto;display: flex;}.second_right {margin-left: 200px;}.third {margin-top: 20px;text-align: center;}#step {font-size: 30px;color: red;display: inline-block;width: 80px;box-sizing: border-box;text-align: center;}.change {width: 100px;height: 40px;font-size: 20px;background-color: #da3c24;border-radius: 10px;color: #fedcdc;}#look {width: 200px;height: 200px;background-repeat: no-repeat;background-size: 100% 100%;border: 4px solid white;}</style>
</head><body><audio src="./audio/bg.mp3" id="bgMusic"></audio><div class="first"><button class="change">更换图片</button><img src="./images/title.png" alt=""></div><div class="second"><div class="second_left"><table><tr><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td></tr></table></div><div class="second_right"><div style="margin-bottom: 40px;"><img src="" id="look"></div><div style="margin-bottom: 30px; font-size: 25px; color:aliceblue">已经走了<span id="step">0</span></div><div><div style="text-align: center;"><img src="./images/shang.png" id="up"></div><div style="text-align: center;"><img src="./images/zuo.png" id="left"><img src="./images/xia.png" id="down"><img src="./images/you.png" id="right"></div></div></div></div><div class="third"><img src="./images/chongzhi.png" id="rest"><img src="./images/qiuzhu.png" id="help"><div style="color: aliceblue;">点击重置按钮(开始游戏或者重新打乱顺序)</div></div><script>let print = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]]let victory = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]];let tds = document.querySelectorAll('td');let isStart = false;let x0 = 0;let y0 = 0;let direction = '';let step = 0;let photo = 'images_1';let leftBtn = document.getElementById('left');let rightBtn = document.getElementById('right');let upBtn = document.getElementById('up');let downBtn = document.getElementById('down');let stepSpan = document.getElementById('step');let helpBtn = document.getElementById('help');let changeBtn = document.querySelector('.change');let look = document.getElementById('look');look.src = './images/images_1/canzhaotu.png';let bgMusic = document.getElementById('bgMusic');window.onkeyup = function (e) {if (isStart) {if (e.keyCode == 37) {direction = 'left';move(direction);} else if (e.keyCode == 38) {direction = 'up';move(direction);} else if (e.keyCode == 39) {direction = 'right';move(direction);} else if (e.keyCode == 40) {direction = 'down';move(direction);}}}let restBtn = document.getElementById('rest');restBtn.onclick = function () {step = 0;isStart = true;shuffle();updateUI();initEvent();helpBtn.onclick = helpBake;bgMusic.play();}leftBtn.onclick = function () {if (isStart) {direction = 'left';move(direction);}}rightBtn.onclick = function () {if (isStart) {direction = 'right';move(direction);}}upBtn.onclick = function () {if (isStart) {direction = 'up';move(direction);}}downBtn.onclick = function () {if (isStart) {direction = 'down';move(direction);}}changeBtn.onclick = function () {isStart = false;print = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]]step=0;let index = parseInt(Math.random() * 4) + 1;let photo_id = look.src.split('/')[look.src.split('/').length - 2].split('_')[1];while (index == photo_id) {index = parseInt(Math.random() * 4) + 1;}photo = 'images_' + index;look.src = "./images/" + photo + "/canzhaotu.png";updateUI();}updateUI();// 更新UIfunction updateUI() {print.forEach((subArr, i) => {subArr.forEach((item, j) => {tds[i * 4 + j].style.backgroundImage = "url(./images/" + photo + "/" + item +".png)";if (isStart && i == x0 && j == y0) {tds[i * 4 + j].style.border = "3px solid red";} else {tds[i * 4 + j].style.border = "";}})})stepSpan.innerHTML = step;if (isStart) {let flag = judgeVictory();if (flag) {alert("恭喜你,成功通关!");isStart = false;}}}// 打乱图片function shuffle() {print.forEach((subArr, i) => {subArr.forEach((item, j) => {let x = parseInt(Math.random() * 4);let y = parseInt(Math.random() * 4);let temp = print[i][j];print[i][j] = print[x][y];print[x][y] = temp;})})}// 选择初始化位置function initEvent() {tds.forEach((td, index) => {td.onclick = function () {let x = parseInt(index / 4);let y = parseInt(index % 4);x0 = x;y0 = y;updateUI();}})}// 移动交换图片function move(direction) {let x, y;if (direction == 'left') {if (y0 - 1 < 0) {console.log("左边到边界了");return;} else {x = x0;y = y0 - 1;}} else if (direction == 'right') {if (y0 + 1 > 3) {console.log("右边到边界了");return;} else {x = x0;y = y0 + 1;}} else if (direction == 'up') {if (x0 - 1 < 0) {console.log("上边到边界了");return;} else {x = x0 - 1;y = y0;}} else if (direction == 'down') {if (x0 + 1 > 3) {console.log("下边到边界了");return;} else {x = x0 + 1;y = y0;}}step++;let temp = print[x][y];print[x][y] = print[x0][y0];print[x0][y0] = temp;// 更新坐标位置x0 = x;y0 = y;updateUI();}// 判断是否胜利function judgeVictory() {for (let i = 0; i < 4; i++) {for (let j = 0; j < 4; j++) {if (print[i][j] != victory[i][j]) {return false;}}}return true;}// 求助按钮function helpBake() {let destX;let destY;let num;wc: for (let i = 0; i < print.length; i++) {for (let j = 0; j < print[i].length; j++) {let index = i * 4 + j;if (print[i][j] != index + 1) {destX = i;destY = j;num = index + 1;break wc;}}}let bakeX;let bakeY;wc: for (let i = 0; i < print.length; i++) {for (let j = 0; j < print[i].length; j++) {if (print[i][j] == num) {bakeX = i;bakeY = j;break wc;}}}x0 = bakeX;y0 = bakeY;updateUI();// 开启自动巡航// 先横向移动,在纵向移动(必须得这样,不然就是bug)helpBtn.onclick = 'none';let timeId = setInterval(() => {// 先比较y0和destYif (y0 < destY) {direction = "right";move(direction);} else if (y0 > destY) {direction = "left";move(direction);} else {if (x0 > destX) {direction = "up";move(direction);} else if (x0 < destX) {direction = "down";move(direction);} else {// 归位了clearInterval(timeId);if (isStart) {helpBtn.onclick = helpBake;}}}}, 800)}</script>
</body></html>

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

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

相关文章

Flink问题解决及性能调优-【Flink不同并行度引起sink2es报错问题】

最近需求&#xff0c;仅想提高sink2es的qps&#xff0c;所以仅调节了sink2es的并行度&#xff0c;但在调节不同算子并行度时遇到一些问题&#xff0c;找出问题的根本原因解决问题&#xff0c;并分析整理。 实例代码 --SET table.exec.state.ttl86400s; --24 hour,默认: 0 ms …

MySQL查询—联合查询、子查询

关于表格的创建&#xff0c;请看上一篇文章——MySQL查询—连接查询 1、联合查询&#xff1a;把多次查询的结果合并&#xff0c;形成一共新的查询集。 UNION&#xff0c;UNION ALL 语法&#xff1a; SELECT 字段列表 FROM 表&#xff21;&#xff0e;&#xff0e;&#…

qt-C++笔记之使用信号和槽实现跨类成员变量同步响应

qt-C笔记之使用信号和槽实现跨类成员变量同步响应 —— 杭州 2024-01-24 code review! 文章目录 qt-C笔记之使用信号和槽实现跨类成员变量同步响应1.运行2.main.cpp3.test.pro4.编译 1.运行 2.main.cpp 代码 #include <QCoreApplication> #include <QObject> #…

Linux下Docker搭建部署Typecho博客【详细版】

Linux下Docker搭建部署Typecho博客【详细版】 一、环境准备1.1.准备阿里云服务器【新用户免费使用三个月】1.2.准备远程工具【FinalShell】1.3.系统信息1.4.安装所需软件包1.5.设置docker镜像源1.6.更新yum软件包索引1.7.确认停用selinux 二、安装Docker2.1.安装Docker-Ce2.2.查…

【RTP】webrtc 学习3: webrtc对h264的rtp解包

rtp_rtcp\source\video_rtp_depacketizer_h264.cc【RTP】webrtc 学习2: webrtc对h264的rtp打包 中分析了打包过程的代码,这样再来看解析过程的源码就容易多了:本代码主要基于m79,m98类似。这里注明了jitterbuffer 会再次 做 解析stap-a 变为NAL units解析ParseFuaNalu 第一…

ACL、VLAN、NAT笔记

一、ACL ---访问控制列表 1.ACL的作用 1&#xff0c;访问控制&#xff1a;在路由器流量流入或流出的接口上&#xff0c;匹配流量&#xff0c;然后 执行设定好的动作。 ---- permit 允许 , deny 拒绝 2&#xff0c;抓取感兴趣流&#xff1a;ACL可以和其他服务结合使用。ACL只…

MyBatis 如何整合 Druid 连接池?

Mybatis 如何整合 Druid 数据连接池呢&#xff1f;首先打开创建的 Maven 工程&#xff0c;找到 pom.xml 文件&#xff0c;添加 Druid 依赖。 <!--druid连接池--> <dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId&…

【C语言】数组的应用:三子棋游戏

由于代码较长&#xff0c;为了增加可读性&#xff0c;我们把代码分别写到game.h&#xff0c;game.c&#xff0c;test.c&#xff0c;里面&#xff0c;其中game.h用来声明函数&#xff0c;实现函数功能的代码在game.c&#xff0c;测试游戏的代码在test.c 为了方便后续的更改&…

ThreadLocal学习笔记

ThreadLocal类图 ThreadLocal/InheritableThreadLocal/ \TransmittableThreadLocal(阿里巴巴) TransmissibleThreadLocal(阿里巴巴)ThreadLocal 这是Thread类的局部变量&#xff0c;每个线程私有。 它主要用于解决多线程中的数据共享问题&#xff0c;保…

k8s 版本发布与回滚

一、实验环境准备&#xff1a; kubectl get pods -o wide kubectl get nodes -o wide kubectl get svc 准备两个nginx镜像&#xff0c;版本号一个是V3&#xff0c;一个是V4 二、准备一个nginx.yaml文件 apiVersion: apps/v1 kind: Deployment metadata:name: nginx-deploylab…

翻译: GPT-4 Vision静态图表转换为动态数据可视化 升级Streamlit 三

GPT-4 Vision 系列: 翻译: GPT-4 with Vision 升级 Streamlit 应用程序的 7 种方式一翻译: GPT-4 with Vision 升级 Streamlit 应用程序的 7 种方式二 1. 将任何静态图表转换为动态数据可视化 ChatGPT Vision 不仅可以将涂鸦变成功能齐全的 Streamlit 应用程序&#xff0c;还…

SpringBoot整合nacos的入门Demo

Nacos介绍 Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称&#xff0c;一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集&#xff0c;帮助您快速…

操作系统(6)----线程相关

目录 1.线程与进程的关系 2.线程的属性 3.线程的实现方式 用户级线程 内核级线程 多线程模型 一对一模型: 多对一模型&#xff1a; 多对多模型&#xff1a; 4.线程的状态和转换 5.线程的组织与控制 1.线程与进程的关系 可以把线程理解为“轻量级进程”。线程是一个…

大创项目推荐 题目:基于卷积神经网络的手写字符识别 - 深度学习

文章目录 0 前言1 简介2 LeNet-5 模型的介绍2.1 结构解析2.2 C1层2.3 S2层S2层和C3层连接 2.4 F6与C5层 3 写数字识别算法模型的构建3.1 输入层设计3.2 激活函数的选取3.3 卷积层设计3.4 降采样层3.5 输出层设计 4 网络模型的总体结构5 部分实现代码6 在线手写识别7 最后 0 前言…

【Spring实战】31 Spring Boot3 集成 Gateway 微服务网关

文章目录 1. 定义2. 功能3. 示例代码1) 创建一个业务服务2&#xff09;创建一个网关服务3&#xff09;启动服务4&#xff09;验证 4. 代码参考结语 1. 定义 Spring Cloud Gateway 是一个基于 Spring Framework 的开源网关服务&#xff0c;用于构建微服务架构中的 API 网关。它…

C51 单片机学习(一):基础外设

参考 51单片机入门教程 1. 单片机简介 1.1 定义 单片机&#xff08;Micro Controller Unit&#xff0c;简称 MCU&#xff09; 内部集成了 CPU、RAM、ROM、定时器、中断系统、通讯接口等一系列电脑的常用硬件功能单片机的任务是信息采集&#xff08;依靠传感器&#xff09;、处…

休息日的思考与额外题——链表

文章目录 前言链表知识点 一、 92. 反转链表 II二、21. 合并两个有序链表总结 前言 一个本硕双非的小菜鸡&#xff0c;备战24年秋招&#xff0c;计划二刷完卡子哥的刷题计划&#xff0c;加油&#xff01; 二刷决定精刷了&#xff0c;于是参加了卡子哥的刷题班&#xff0c;训练…

富文本编辑器CKEditor4简单使用-01

富文本编辑器CKEditor4简单使用-01 1. 快速体验入门1.1 通过从 CDN 加载 CKEditor 来快速体验1.2 从官方网站下载软件包1.2.1 官网下载1.2.2 解压、简单使用&#xff08;自带index页面示例&#xff09;1.2.3 将 CKEditor 4 添加到自己的页面1.2.3.1 目录结构1.2.3.2 效果1.2.3.…

TensorFlow2实战-系列教程6:迁移学习实战

&#x1f9e1;&#x1f49b;&#x1f49a;TensorFlow2实战-系列教程 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Jupyter Notebook中进行 本篇文章配套的代码资源已经上传 1、迁移学习 用已经训练好模型的权重参数当做自己任务的模型权重初始化一般全连接层需…

【机器学习】工程实践问题概述

机器学习实际应用时的工程问题与面临的挑战 一、实现细节问题 1.1 训练样本 训练样本与标注对各类机器学习算法和模型的精度影响 训练样本的选择对各类机器学习算法和模型的影响 训练样本的优化 如何进行数据增强&#xff1f; 如何进行数据清洗&#xff1f; 样本的标注对各类机…