web效果图
web实现步骤
说明
首先这个设计的尺寸是w1200,h680,点位的配置也是针对这个尺寸的基础上去配置
点位的配置没有做灵活的,如果要做的话,正常来讲是在图片上点击选取点位,目前是用最简单输入框的方式配置
1、绘制带有圆角的矩形,也就是对话框
个人也是第一次用canvas,不是非常熟悉,觉得最难的部分在绘图,前端页面样式更多是用css的方式书写,但现在要用画布的方式去绘制,比如要绘制矩形,绘制圆角,绘制倒三角,文字填充等,不会写,各种百度
// 带有圆角、还有boxshadw效果的对话框
const drawRoundedRect = (ctx, x, y, width, height, radius,shadow) => {
// 开始绘制路径
ctx.beginPath()
// 左上角圆角
ctx.moveTo(x + radius, y)
ctx.lineTo(x + width - radius, y)
ctx.quadraticCurveTo(x + width, y, x + width, y + radius)
// 右上角圆角
ctx.lineTo(x + width, y + height - radius)
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height)
// 右下角圆角
ctx.lineTo(x + radius, y + height)
ctx.quadraticCurveTo(x, y + height, x, y + height - radius)
// 左下角圆角
ctx.lineTo(x, y + radius)
ctx.quadraticCurveTo(x, y, x + radius, y)
// 关闭路径
ctx.closePath()
if (shadow == 1) {
ctx.shadowBlur = 30 // 阴影模糊度
ctx.shadowColor = 'rgba(0,0,0,.2)' // 阴影颜色
} else {
ctx.shadowBlur = 0 // 阴影模糊度
ctx.shadowColor = '' // 阴影颜色
}
// 填充矩形
ctx.fill()
}
const copyPointFun = (arr) => {
if (arr) {
arr.forEach((item) => {
item.x = item.pointX
item.y = item.pointY
})
return arr
}
}
// 根据宽高比比例计算实际点位位置
const pointsFun = (bw, bh, arr) => {
let webWidth = 1200
let webHeight = 680
let screenWidth = bw //屏幕宽
let screenHeight = bh // 屏幕高
arr.forEach((item) => {
let webx = webWidth / item.pointX
let weby = webHeight / item.pointY
let pintX = screenWidth / webx
let pintY = screenHeight / weby
item.x = pintX
item.y = pintY
})
return arr
}
// 假数据
const points = ref([
{
id: 1260195,
groupId: 1290194,
x: 530,
y: 470,
name: '班会课班会陆班会课会课陆课会课',
type: 0,
state: 0,
respondTimes: 2,
reuploadFlag: 1,
beginTime: '2024-05-30 00:00:00',
endTime: '2024-06-30 00:00:00',
width: 128,
height: 77,
}, //班会课
{
id: 1290187,
groupId: 1290194,
x: 800,
y: 460,
name: '校门口',
type: 0,
state: 0,
respondTimes: 2,
reuploadFlag: 0,
beginTime: '2024-05-30 00:00:00',
endTime: '2024-06-30 00:00:00',
width: 128,
height: 77,
}, //校门口
{
id: 1260194,
groupId: 1290194,
x: 220,
y: 440,
name: '卫生角',
type: 0,
state: 0,
respondTimes: 2,
reuploadFlag: 0,
beginTime: '2024-05-30 00:00:00',
endTime: '2024-06-30 00:00:00',
width: 128,
height: 77,
}, //卫生角
{
id: 1290186,
groupId: 1290194,
x: 300,
y: 230,
name: '黑板报',
type: 0,
state: 0,
respondTimes: 2,
reuploadFlag: 0,
beginTime: '2024-05-30 00:00:00',
endTime: '2024-06-30 00:00:00',
width: 128,
height: 77,
}, //黑板报
{
id: 1260196,
groupId: 1290194,
x: 685,
y: 165,
name: '办公室',
type: 0,
state: 0,
respondTimes: 2,
reuploadFlag: 0,
beginTime: '2024-05-30 00:00:00',
endTime: '2024-06-12 00:00:00',
width: 128,
height: 77,
}, //办公室
{
id: 1290188,
groupId: 1290194,
x: 920,
y: 370,
name: '传达室',
type: 0,
state: 0,
respondTimes: 2,
reuploadFlag: 0,
beginTime: '2024-05-30 00:00:00',
endTime: '2024-06-06 00:00:00',
width: 128,
height: 77,
}, //传达室
])
//判断是否超过9个字符,超过了就省略号
const stringLength = (val) => {
let text
if (val && val.length < 7) {
// console.log('11111')
return val
} else if (val && val.length > 6 && val.length < 10) {
// console.log('22222')
//大于12个字符加换行符
const charToInsert = '\n'
const positionOth = 6
const newResult = insertCharAt(val, charToInsert, positionOth)
const text = newResult + '...'
return text
} else if (val && val.length > 6 && val.length > 10) {
// console.log('33333')
const originalString = val
// 在第20个字符那拼接省略号
const position = 10
const result = getSubstringBefore(originalString, position)
const newResult = result + '...'
// 在第12个字符那插入换行符
const charToInsert = '\n'
const positionOth = 6
text = insertCharAt(newResult, charToInsert, positionOth)
return text
}
}
前面封装了方法,还有假数据,接下来就是写代码,在方法里调用了
const canvasFun = () => {
// 获取当前屏幕宽度
const screenWidth = window.screen.width
const screenHeight = window.screen.height
// 这里是针对pad尺寸的适配
// 小于1281 是pad尺寸
if (screenWidth < 1281) {
// console.log('我不是pc')
const boxs = document.querySelector('.second')
var offsetLeft = boxs.offsetLeft
var boxWidth = screenWidth - offsetLeft - 32 // 剪掉当前盒子距离左侧菜单及2边padding尺寸
var boxHeight = screenHeight - 272 // 剪掉canvas距离顶部、底部的距离
isPc.value = false
} else {
// console.log('我是pc')
isPc.value = true
}
const canvas = document.querySelector('#canvas')
const ctx = canvas.getContext('2d')
const image = new Image()
if (basicInfo.value.backgroundImage) {
// console.log('有图片了')
const url = `${publicUrl.getFile}?fileName=${
basicInfo.value.backgroundImage
}&dispositionType=2&satoken=${Cookies.get('satoken')}`
image.src = url // 图片链接
// 监听图片加载完成
image.onload = function () {
if (isPc.value == true) {
canvas.width = 1200
canvas.height = 680
newArr.value = copyPointFun(basicInfo.value.chooseSubjectList)
} else {
canvas.width = boxWidth
canvas.height = boxHeight
// 去计算比例
newArr.value = pointsFun(
boxWidth,
boxHeight,
basicInfo.value.chooseSubjectList
)
}
ctx.drawImage(image, 0, 0, canvas.width, canvas.height)
// 添加点位交互
newArr.value.forEach((point) => {
//--------------设置高度
let diaheight = setCanvasDiaHeight(point.name)
// // 设置填充颜色为黄色
ctx.fillStyle = '#ffffff'
// // -------------绘制矩形
let pointX = point.x - 50 //实际点位再偏移
let pointY = point.y - diaheight
console.log(pointX,pointY,'盒子的位置')
//创建一个填充矩形(弹框)
drawRoundedRect(ctx, pointX, pointY, 128, diaheight, 14,1)
ctx.font = '16px AlibabaPuHuiTi' // 设置字体大小和颜色
ctx.fontWeight = 'bold' // 再设置字重
ctx.fillStyle = '#181A1C' // 设置字体大小和颜色
ctx.textAlign = 'center' // 设置文本水平居中
ctx.textBaseline = 'middle' // 设置文本基线
// 处理标题名称,插入换行符
let pointName = stringLength(point.name)
// 在换行处拆分文本并多次调用 fillText() 来模拟换行
var lineheight = 18
var lines = pointName.split('\n')
// console.log(lines,'lines')
// 在弹框中间写入文本
for (var i = 0; i < lines.length; i++) {
if (i == 0) {
ctx.fillText(lines[i], pointX + 64, pointY + 30 + i * lineheight)
} else {
ctx.fillText(lines[i], pointX + 54, pointY + 30 + i * lineheight)
}
}
})
}
}
}
onMounted(() => {
canvasFun()
})
setCanvasDiaHeight()方法是动态设置高度,因为盒子的高度会存在2种尺寸
drawRoundedRect(ctx, pointX, pointY, 128, diaheight, 14,1)
pointX, pointY 是实际点位,这里128是对话框的宽度,diaheight 是动态设置高,14是圆角,1是是否要盒子阴影
到目前位置只是绘制的一部分,只是一个矩形,和填充了标题,因为标题可能过长,这里stringLength()方法也处理了过长加换行和省略号
2、绘制下三角
const canvasFun = () => {
// 获取当前屏幕宽度
const screenWidth = window.screen.width
const screenHeight = window.screen.height
// 这里是针对pad尺寸的适配
// 小于1281 是pad尺寸
if (screenWidth < 1281) {
// console.log('我不是pc')
const boxs = document.querySelector('.second')
var offsetLeft = boxs.offsetLeft
var boxWidth = screenWidth - offsetLeft - 32 // 剪掉当前盒子距离左侧菜单及2边padding尺寸
var boxHeight = screenHeight - 272 // 剪掉canvas距离顶部、底部的距离
isPc.value = false
} else {
// console.log('我是pc')
isPc.value = true
}
const canvas = document.querySelector('#canvas')
const ctx = canvas.getContext('2d')
const image = new Image()
if (basicInfo.value.backgroundImage) {
// console.log('有图片了')
const url = `${publicUrl.getFile}?fileName=${
basicInfo.value.backgroundImage
}&dispositionType=2&satoken=${Cookies.get('satoken')}`
image.src = url // 图片链接
// 监听图片加载完成
image.onload = function () {
if (isPc.value == true) {
canvas.width = 1200
canvas.height = 680
newArr.value = copyPointFun(basicInfo.value.chooseSubjectList)
} else {
canvas.width = boxWidth
canvas.height = boxHeight
// 去计算比例
newArr.value = pointsFun(
boxWidth,
boxHeight,
basicInfo.value.chooseSubjectList
)
}
ctx.drawImage(image, 0, 0, canvas.width, canvas.height)
// 添加点位交互
newArr.value.forEach((point) => {
//--------------设置高度
let diaheight = setCanvasDiaHeight(point.name)
// // 设置填充颜色为黄色
ctx.fillStyle = '#ffffff'
// // -------------绘制矩形
let pointX = point.x - 50 //实际点位再偏移
let pointY = point.y - diaheight
console.log(pointX,pointY,'盒子的位置')
//创建一个填充矩形(弹框)
drawRoundedRect(ctx, pointX, pointY, 128, diaheight, 14,1)
ctx.font = '16px AlibabaPuHuiTi' // 设置字体大小和颜色
ctx.fontWeight = 'bold' // 再设置字重
ctx.fillStyle = '#181A1C' // 设置字体大小和颜色
ctx.textAlign = 'center' // 设置文本水平居中
ctx.textBaseline = 'middle' // 设置文本基线
// 处理标题名称,插入换行符
let pointName = stringLength(point.name)
// 在换行处拆分文本并多次调用 fillText() 来模拟换行
var lineheight = 18
var lines = pointName.split('\n')
// console.log(lines,'lines')
// 在弹框中间写入文本
for (var i = 0; i < lines.length; i++) {
if (i == 0) {
ctx.fillText(lines[i], pointX + 64, pointY + 30 + i * lineheight)
} else {
ctx.fillText(lines[i], pointX + 54, pointY + 30 + i * lineheight)
}
}
// -------------绘制倒三角
// 气泡框的起点坐标 (x, y)
var x = point.x // 40是弹框宽度的一半
var y = point.y // 55是弹框的高度
// 倒三角的大小
var width = 24
var height = 17
// 开始绘制
ctx.beginPath()
// 绘制倒三角形
ctx.moveTo(x, y) // 气泡框左上角
ctx.lineTo(x + width, y) // 气泡框右上角
ctx.lineTo(x + width / 2, y + height) // 气泡框中间底部
ctx.lineTo(x, y) // 闭合路径
// 设置三角形颜色并填充
ctx.fillStyle = '#FFFFFF'
ctx.fill()
})
}
}
}
onMounted(() => {
canvasFun()
})
3、绘制参与按钮
const canvasFun = () => {
// 获取当前屏幕宽度
const screenWidth = window.screen.width
const screenHeight = window.screen.height
// 这里是针对pad尺寸的适配
// 小于1281 是pad尺寸
if (screenWidth < 1281) {
// console.log('我不是pc')
const boxs = document.querySelector('.second')
var offsetLeft = boxs.offsetLeft
var boxWidth = screenWidth - offsetLeft - 32 // 剪掉当前盒子距离左侧菜单及2边padding尺寸
var boxHeight = screenHeight - 272 // 剪掉canvas距离顶部、底部的距离
isPc.value = false
} else {
// console.log('我是pc')
isPc.value = true
}
const canvas = document.querySelector('#canvas')
const ctx = canvas.getContext('2d')
const image = new Image()
if (basicInfo.value.backgroundImage) {
// console.log('有图片了')
const url = `${publicUrl.getFile}?fileName=${
basicInfo.value.backgroundImage
}&dispositionType=2&satoken=${Cookies.get('satoken')}`
image.src = url // 图片链接
// 监听图片加载完成
image.onload = function () {
if (isPc.value == true) {
canvas.width = 1200
canvas.height = 680
newArr.value = copyPointFun(basicInfo.value.chooseSubjectList)
} else {
canvas.width = boxWidth
canvas.height = boxHeight
// 去计算比例
newArr.value = pointsFun(
boxWidth,
boxHeight,
basicInfo.value.chooseSubjectList
)
}
ctx.drawImage(image, 0, 0, canvas.width, canvas.height)
// 添加点位交互
newArr.value.forEach((point) => {
//--------------设置高度
let diaheight = setCanvasDiaHeight(point.name)
// // 设置填充颜色为黄色
ctx.fillStyle = '#ffffff'
// // -------------绘制矩形
let pointX = point.x - 50 //实际点位再偏移
let pointY = point.y - diaheight
console.log(pointX,pointY,'盒子的位置')
//创建一个填充矩形(弹框)
drawRoundedRect(ctx, pointX, pointY, 128, diaheight, 14,1)
ctx.font = '16px AlibabaPuHuiTi' // 设置字体大小和颜色
ctx.fontWeight = 'bold' // 再设置字重
ctx.fillStyle = '#181A1C' // 设置字体大小和颜色
ctx.textAlign = 'center' // 设置文本水平居中
ctx.textBaseline = 'middle' // 设置文本基线
// 处理标题名称,插入换行符
let pointName = stringLength(point.name)
// 在换行处拆分文本并多次调用 fillText() 来模拟换行
var lineheight = 18
var lines = pointName.split('\n')
// console.log(lines,'lines')
// 在弹框中间写入文本
for (var i = 0; i < lines.length; i++) {
if (i == 0) {
ctx.fillText(lines[i], pointX + 64, pointY + 30 + i * lineheight)
} else {
ctx.fillText(lines[i], pointX + 54, pointY + 30 + i * lineheight)
}
}
// -------------绘制倒三角
// 气泡框的起点坐标 (x, y)
var x = point.x // 40是弹框宽度的一半
var y = point.y // 55是弹框的高度
// 倒三角的大小
var width = 24
var height = 17
// 开始绘制
ctx.beginPath()
// 绘制倒三角形
ctx.moveTo(x, y) // 气泡框左上角
ctx.lineTo(x + width, y) // 气泡框右上角
ctx.lineTo(x + width / 2, y + height) // 气泡框中间底部
ctx.lineTo(x, y) // 闭合路径
// 设置三角形颜色并填充
ctx.fillStyle = '#FFFFFF'
ctx.fill()
//--------------------设置按钮
var btnheight = 20
// 设置圆角的半径
var radius = 10
ctx.beginPath()
let obj = showBtnName.value(point)
//-------------------是否有带重传标志,再来设置,矩形的宽度和高度
if (point.reuploadFlag == 1) {
drawRoundedRect(ctx, point.x - 35, point.y - 30, 43, btnheight, radius,0)
} else {
// 填充矩形
drawRoundedRect(ctx, point.x - 30, point.y - 30, 96, btnheight, radius,0)
}
let color = showBtnBgc(obj.label)
ctx.fillStyle = color
ctx.fill()
})
})
}
}
}
onMounted(() => {
canvasFun()
})
这里的showBtnName是动态判断名称,因为按钮文本状态很多,showBtnBgc是根据状态去动态写入不同的颜色
4、设置文字颜色
newArr.value.forEach((point) => {
ctx.beginPath()
let obj = showBtnName.value(point)
let color = showBtnColor(obj.label)
ctx.fillStyle = color
ctx.font = '12px AlibabaPuHuiTi' // 设置字体大小和颜色
if (point.reuploadFlag == 1) {
ctx.fillText(obj.label, point.x - 15, point.y - 18)
// 绘制带重传按钮
ctx.fillStyle = '#E0831F'
ctx.fillText('待重传', point.x + 40, point.y - 18)
ctx.closePath()
ctx.fill()
} else {
let leftX = setleft(obj.label, point.x)
ctx.fillText(obj.label, leftX, point.y - 18)
ctx.textAlign = 'center' // 设置文本水平居中
ctx.textBaseline = 'middle' // 设置文本基线
ctx.closePath()
ctx.fill()
}
})
5、 根据不同状态去展示不同图标,这个图标指得是矩形左上角得图片
const iconIndexFun = (val) => {
if (val == '完成') {
return 2
} else if (val == '已放弃') {
return 1
} else if (val == '查看') {
return 0
} else if (val == '待解锁') {
return 3
} else if (val == '参与' || val == '再次作答') {
return 4
} else if (val == '过期') {
return 5
} else {
return 6 //代表没有
}
}
// 图标数据
const pointImageURLs = [
require('@/assets/image/second-image/icon/read.png'),
require('@/assets/image/second-image/icon/giveUp.png'),
require('@/assets/image/second-image/icon/finish.png'),
require('@/assets/image/second-image/icon/lock.png'),
require('@/assets/image/second-image/icon/exclamationMark.png'),
require('@/assets/image/second-image/icon/overdue.png'),
]
newArr.value.forEach((point) => {
let obj = showBtnName.value(point)
iconIndex.value = iconIndexFun(obj.label)
let diaheight = setCanvasDiaHeight(point.name)
let pointX = point.x - 60 //实际点位再偏移
let pointY = point.y - diaheight - 7
if(iconIndex.value==6||iconIndex.value==4){
let show = show7Days.value(point)
if(show){
//展示7天截至图标
const img = new Image();
img.src = pointImageURLs[4];
img.onload = () => {
ctx.drawImage(img, pointX, pointY,28,28);
ctx.fill()
}
}else{
// 不展示任何图标
return
}
}else {
const img = new Image();
img.src = pointImageURLs[iconIndex.value];
img.onload = () => {
ctx.drawImage(img, pointX, pointY,28,28);
ctx.fill()
}
}
})
6、 绘制7天截至矩形盒子
newArr.value.forEach((point) => {
let show = show7Days.value(point)
if (show == true) {
let diaheight = setCanvasDiaHeight(point.name)
ctx.beginPath()
// 创建渐变对象
const gradient = ctx.createLinearGradient(0, 0, 0, 18)
gradient.addColorStop(0, 'rgba(255,255,255,1)') // 渐变起始颜色
gradient.addColorStop(1, 'rgba(255,226,220,1)') // 渐变结束颜色
// 设置渐变为填充样式
ctx.fillStyle = gradient
drawRoundedRect(
ctx,
point.x - 50,
point.y - diaheight - 3,
76,
18,
10,
0
)
ctx.fill()
ctx.strokeStyle = 'white'
ctx.stroke()
} else {
return
}
})
7、点击对话框进入对应页面
最初需求是点按钮进入,后来改了点击矩形区域就能进入对应页面
onMounted(() => {
canvasFun()
const canvas = document.querySelector('#canvas')
const ctx = canvas.getContext('2d')
canvas.addEventListener('mousedown', function (e) {
for (var i = 0; i < newArr.value.length; i++) {
var point = newArr.value[i]
// 鼠标在屏幕上的位置
var mouseX = e.clientX;
var mouseY = e.clientY;
// 鼠标相对于canvas的位置
var rect = canvas.getBoundingClientRect();
var canvasX = mouseX - rect.left - canvas.clientLeft;
var canvasY = mouseY - rect.top - canvas.clientTop;
// 转换为实际的绘图坐标(考虑缩放和平移等情况)
var actualX = canvasX / canvas.offsetWidth * ctx.canvas.width;
var actualY = canvasY / canvas.offsetHeight * ctx.canvas.height;
//-------------获取高度
let diaheight = setCanvasDiaHeight(point.name)
// 矩形的起始点位
let pointX = point.x - 50 //实际点位再偏移
let pointY = point.y - diaheight
let rangeX = pointX + 128
let rangeY = pointY + diaheight
// 计算点击范围
if(actualX > pointX &&actualX < rangeX && actualY > pointY && actualY < rangeY){
console.log(newArr.value[i],'拿到对应点位信息')
//----下面就是进入对应页面的逻辑了
}
}
})
})
这里需要说下,我们实际的点位是图片上倒三角指向的地方(也就是红色圈),所以是要算出实际矩形的点位的,
而矩形的起始点位,也就是先拿到矩形左边定点的位置 pointX,
比如这个点位是100,而宽高都是已知的,就能算出实际矩形所在的点位了
所以用顶点位置+宽=右上边的位置,
用顶点位置+高=得到左下方的位置,
再用左下方的点位+宽=得到右下方的位置,
再最后判断event实际点击的位置是否在这个范围了,这就可以了。
结束
web的基本就到这了,经过测试,这套算法是没问题的,屏幕缩放120%、150%也好,都不影响,并且也适配pad端,至于小屏适配,走h5的那套算法
简单的说,在标注的点位上用canvas实现了全部的样式和交互
虽然是麻烦了点,但确实是实现了,暂时目前没有想到是否还有其他的方法去实现,因为需求是希望配置的1套点位,适配所有端。
当然这套代码里也是有冗余部分,画笔绘制完,需要关闭路径,才能画下一个内容,否则会有冲突,会出现样式相互影响的情况,为避免干扰,我采用的是再次循环的方法去写入样式,当然这样肯定是耗费性能的,目前暂时没想到更好的处理办法。
h5效果图
h5实现步骤
说明
样式的绘制跟web一样
不同的是,移动端点位的等比例适配计算、点击触发范围的计算,别的不变
1、移动端点位的等比例适配计算
a、 1200/手机横屏后的高=x宽高比
680/手机横屏后的宽=y宽高比
b、再用x轴点位/x宽高比 =实际展示x点位
y轴就是用y轴点位/y宽高比=实际展示y点位
举例:
点位 x 590
y370
屏幕设备 宽414高896 (h5效果是要横屏过来,实际计算的时候 896是宽,414是高)
1200/896=1.33
680/414=1.64
x 590/1.33= 443.6
y 370/1.64=225.6
而443.6是移动端要展示的x点位,225.6是y的点位
// 根据宽高比比例计算实际点位位置
const pointsFun=(arr)=>{
let webWidth= 1200
let webHeight=680
let screenWidth = window.innerHeight //屏幕宽
let screenHeight = window.innerWidth // 屏幕高
arr.forEach(item=>{
let webx = webWidth / item.pointX
let weby = webHeight / item.pointY
let pintX= screenWidth / webx
let pintY= screenHeight / weby
item.pintX=pintX
item.pintY=pintY
})
return arr
}
// 监听图片加载完成
image.onload = function () {
// 这里要获取屏幕的可视宽高作为canvas的宽高
canvas.width = window.innerHeight
canvas.height = window.innerWidth
ctx.drawImage(image, 0, 0, canvas.width, canvas.height)
// 这里是去处理点位的适配计算
newArr.value=pointsFun(basicInfo.value.chooseSubjectList)
// 根据点位写入样式
newArr.value.forEach((point) => {
...
})
}
这里横屏效果css样式
// 装canvas的父盒子
.interactiveActivitySecond {
position:relative;
width: 100vh;
height: 100vw;
transform-origin: 0 0;//主要是这个
transform: rotateZ(90deg) translateY(-100%);//主要是这个
}
2、点击触发范围
没有像web一样去计算他的矩形实际宽高点位计算
是直接设置一个值60,以这个值去判断他的半径
canvas.addEventListener('mousedown', function (e) {
const radius = 60
// 遍历所有标注点
for (var i = 0; i < newArr.value.length; i++) {
var point = newArr.value[i]
// 计算点击坐标与标注点坐标的距离
var dx = e.offsetX - point.pintX
var dy = e.offsetY - point.pintY + 50
var distance = Math.sqrt(dx * dx + dy * dy)
// 如果距离小于半径,则认为点击在标注点上
if (distance < radius) {
console.log('代表点击到了,去对应页面')
}
}
})
结束。