当前位置: 首页 > wzjs >正文

博物馆网站微信公众号建设方案网站空间到期 数据

博物馆网站微信公众号建设方案,网站空间到期 数据,企业差旅服务平台,北京软件有限公司涉及仪器的预约使用,仿照小米日历日程预约开发开发对应页。 效果展示 文章目录 效果展示需求分析代码实现一、构建基础页面结构1. 顶部日期选择器2. 中部canvas绘制3. 底部数据回显 二、中间canvas功能细分1. 激活状态的判断2. 时间块拉伸逻辑3. 时间块拖动逻辑 三…

涉及仪器的预约使用,仿照小米日历日程预约开发开发对应页。

效果展示

在这里插入图片描述

文章目录

  • 效果展示
  • 需求分析
  • 代码实现
  • 一、构建基础页面结构
    • 1. 顶部日期选择器
    • 2. 中部canvas绘制
    • 3. 底部数据回显
  • 二、中间canvas功能细分
    • 1. 激活状态的判断
    • 2. 时间块拉伸逻辑
    • 3. 时间块拖动逻辑
  • 三、底部数据回显
  • 总结


需求分析

  • 顶部七日选择器
    • 横向显示从当前日期开始后的七天,并区分月-日
    • 七天共计预约时间段综合为3
  • 中部canvas绘制区
    • 左侧时间刻度
    • 右侧绘制区,总计24格,每大格为1h,一大格后期拆分四小格,为15min
    • 右侧绘制区功能
      • 激活:单击
      • 长按:拖动激活区域移动选区,存在激活区域之间的互斥
      • 拉伸:双击后改变预约起止时间
  • 底部数据回显区
    • 显示预约时间段
    • 支持删除

代码实现

一、构建基础页面结构

在这里插入图片描述

1. 顶部日期选择器

获取当前日期,即六天后的所有日期,并解析出具体月-日,存入数组dateList

 // 初始化日期列表initDateList() {const dateList = [];const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];for (let i = 0; i < 7; i++) {const date = new Date();// 获取未来几天的日期date.setDate(date.getDate() + i);dateList.push({date: date.getTime(),month: date.getMonth() + 1,day: date.getDate(),weekDay: weekDays[date.getDay()]});}this.setData({ dateList });},
<view wx:for="{{ dateList }}" wx:key="date"class="date-item {{ currentDateIndex === index ? 'active' : '' }}"bindtap="onDateSelect"data-index="{{ index }}"><text class="date-text">{{ item.month }}-{{ item.day }}</text><text class="week-text">{{ item.weekDay }}</text><text class="today-text" wx:if="{{ index === 0 }}">今天</text></view>

2. 中部canvas绘制

左侧25条数据,从0:00-24:00,只作为标志数据;【主体】右侧24格,通过canvas进行绘制。

  1. 初始化canvas,获取宽高,并通过ctx.scale(dpr,dpr)缩放canvas适应设备像素比;
  2. 绘制网格
   for (let i = 0; i <= 24; i++) {ctx.beginPath();const y = i * hourHeight;ctx.moveTo(0, y);ctx.lineTo(width, y);ctx.stroke();}

3. 底部数据回显

二、中间canvas功能细分

1. 激活状态的判断

  1. 首先给canvas添加点击事件bindtouchstart="onCanvasClick"

获取点击坐标,并解析首次触摸点的位置touch[0]clientX clientY 是触摸点在屏幕上的坐标

const query = wx.createSelectorQuery();
query.select('#timeGridCanvas').boundingClientRect(rect => {const x = e.touches[0].clientX - rect.left;const y = e.touches[0].clientY - rect.top;
  1. 计算时间格
const hourIndex = Math.floor(y / this.data.hourHeight);

hourHeight: rect.height / 24,来自于initCanvas初始化时,提前计算好的每个时间格的高度

  1. 获取选中的时间段
const existingBlockIndex = this.data.selectedBlocks.findIndex(block => hourIndex >= block.startHour && hourIndex < block.endHour);

使用 findIndex 查找点击位置是否在已选时间段内

  1. 取消选中逻辑
if (existingBlockIndex !== -1) {// 从当前日期的选中块中移除const newSelectedBlocks = [...this.data.selectedBlocks];newSelectedBlocks.splice(existingBlockIndex, 1);// 从所有选中块中移除const currentDate = `${this.data.dateList[this.data.currentDateIndex].month}-${this.data.dateList[this.data.currentDateIndex].day}`;const allBlockIndex = this.data.allSelectedBlocks.findIndex(block => block.date === currentDate && block.startHour === this.data.selectedBlocks[existingBlockIndex].startHour);const newAllBlocks = [...this.data.allSelectedBlocks];if (allBlockIndex !== -1) {newAllBlocks.splice(allBlockIndex, 1);}this.setData({selectedBlocks: newSelectedBlocks,allSelectedBlocks: newAllBlocks});
}

同时需要考虑两个数组:当前日期选中时间段selectedBlocks,七日内选中时间段总数allSelectedBlocks

  1. 新增时间段逻辑
else {// 检查限制if (this.data.allSelectedBlocks.length >= 3) {wx.showToast({title: '最多只能选择3个时间段',icon: 'none'});return;}// 添加新时间段const startHour = Math.floor(y / this.data.hourHeight);const endHour = startHour + 1;const newBlock = {date: `${this.data.dateList[this.data.currentDateIndex].month}-${this.data.dateList[this.data.currentDateIndex].day}`,startHour: startHour,endHour: endHour,startTime: this.formatTime(startHour * 60),endTime: this.formatTime(endHour * 60)};this.setData({selectedBlocks: [...this.data.selectedBlocks, newBlock],allSelectedBlocks: [...this.data.allSelectedBlocks, newBlock]});
}

先检查是否达到最大选择限制,创建新的时间段对象

date: 当前选中的日期
startHour: 开始小时
endHour: 结束小时
startTime: 格式化后的开始时间
endTime: 格式化后的结束时间

2. 时间块拉伸逻辑

  1. 检测拉伸手柄
    为了避免和后期的长按拖动逻辑的冲突,在选中时间块上额外添加上下手柄以作区分:
checkResizeHandle(x, y) {const handleSize = 16; // 手柄的点击范围大小for (let i = 0; i < this.data.selectedBlocks.length; i++) {const block = this.data.selectedBlocks[i];const startY = block.startHour * this.data.hourHeight;const endY = block.endHour * this.data.hourHeight;// 检查是否点击到上方手柄if (y >= startY - handleSize && y <= startY + handleSize) {return { blockIndex: i, isStart: true, position: startY };}// 检查是否点击到下方手柄if (y >= endY - handleSize && y <= endY + handleSize) {return { blockIndex: i, isStart: false, position: endY };}}return null;
}
  1. 处理拖拽拉伸逻辑
    在判断确定点击到拉伸手柄的情况下,处理逻辑
const resizeHandle = this.checkResizeHandle(x, y);if (resizeHandle) {// 开始拉伸操作this.setData({isResizing: true,resizingBlockIndex: resizeHandle.blockIndex,startY: y,initialY: resizeHandle.position,isResizingStart: resizeHandle.isStart});return;}
isResizing:标记正在拉伸
startY:开始拖动的位置
initialY:手柄的初始位置
isResizingStart:是否在调整开始时间
  1. 处理拖动过程
    需要根据拖动的距离来计算新的时间,将拖动的距离转换成时间的变化。简单来说,假设一小时占60px的高度,那么15min=15px,如果用户往下拖动30px,换算成时间就是30min。
// 计算拖动了多远
const deltaY = currentY - startY;  // 比如拖动了30像素// 计算15分钟对应的高度
const quarterHeight = hourHeight / 4;  // 假设hourHeight是60,那么这里是15// 计算移动了多少个15分钟
const quarterMoved = Math.floor(Math.abs(deltaY) / quarterHeight) * (deltaY > 0 ? 1 : -1);// 计算新的时间
const newTime = originalTime + (quarterMoved * 0.25);  // 0.25代表15分钟
  1. 更新时间显示
    计算出新的时间后,需要在确保有效范围内的同时,对齐15min的刻度并转化显示格式
// 确保时间合理,比如不能小于0点,不能超过24点
if (newTime >= 0 && newTime <= 24) {// 对齐到15分钟const alignedTime = Math.floor(newTime * 4) / 4;// 转换成"HH:MM"格式const hours = Math.floor(alignedTime);const minutes = Math.round((alignedTime - hours) * 60);const timeString = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
}
  1. 结束拉伸逻辑
    当松手时,清楚拖动状态,将标识符置false
    this.setData({
    isResizing: false, // 结束拖动状态
    resizingBlockIndex: null, // 清除正在拖动的时间块
    startY: 0 // 重置起始位置
    });

3. 时间块拖动逻辑

  1. 长按时间块
    首先找到点击的时间块并存储信息,在原视图上”删除“该时间块,并标记拖动状态
onCanvasLongPress(e) {// 1. 先找到用户点击的是哪个时间块const hourIndex = Math.floor(y / this.data.hourHeight);const pressedBlockIndex = this.data.selectedBlocks.findIndex(block => hourIndex >= block.startHour && hourIndex < block.endHour);// 2. 如果真的点到了时间块if (pressedBlockIndex !== -1) {// 3. 保存这个时间块的信息,因为待会要用const pressedBlock = {...this.data.selectedBlocks[pressedBlockIndex]};// 4. 从原来的位置删除这个时间块const newBlocks = [...this.data.selectedBlocks];newBlocks.splice(pressedBlockIndex, 1);// 5. 设置拖动状态this.setData({isDragging: true,                // 标记正在拖动dragBlock: pressedBlock,         // 保存被拖动的时间块dragStartY: y,                   // 记录开始拖动的位置selectedBlocks: newBlocks,       // 更新剩下的时间块dragBlockDuration: pressedBlock.endHour - pressedBlock.startHour  // 记录时间块长度});}
}
  1. 时间块投影
    为了区分正常激活时间块,将长按的以投影虚化方式显示,提示拖动结束的位置。
    首先计算触摸移动的距离,并根据上文,推测相应时间变化。在合理的范围内,检测是否和其他时间块互斥,最终更新时间块的显示。
onCanvasMove(e) {if (this.data.isDragging) {const y = e.touches[0].clientY - rect.top;const deltaY = y - this.data.dragStartY;const quarterHeight = this.data.hourHeight / 4;const quarterMoved = Math.floor(deltaY / quarterHeight);const targetHour = this.data.dragBlock.startHour + (quarterMoved * 0.25);const boundedHour = Math.max(0, Math.min(24 - this.data.dragBlockDuration, targetHour));const isOccupied = this.checkTimeConflict(boundedHour, boundedHour + this.data.dragBlockDuration);this.setData({dragShadowHour: boundedHour,     // 投影的位置dragShadowWarning: isOccupied    // 是否显示冲突警告});}
}
  1. 互斥检测
    排除掉当前拖动时间块,检测与其余是否重叠。
    具体来说,假设当前时间块9:00-10:00,新位置9:30-10:30,这种情况 startHour(9:30) < block.endHour(10:00)endHour(10:30) > block.startHour(9:00)所以检测为重叠
checkTimeConflict(startHour, endHour) {return this.data.selectedBlocks.some(block => {if (block === this.data.dragBlock) return false;return (startHour < block.endHour && endHour > block.startHour);});
}
  1. 结束拖动
    当位置不互斥,区域有效的情况下,放置新的时间块,并添加到列表中,最后清理所有拖动相关的状态
onCanvasEnd(e) {if (this.data.isDragging) {if (this.data.dragShadowHour !== null && this.data.dragBlock && !this.data.dragShadowWarning) {const newHour = Math.floor(this.data.dragShadowHour * 4) / 4;const duration = this.data.dragBlockDuration;const newBlock = {startHour: newHour,endHour: newHour + duration,startTime: this.formatTime(Math.round(newHour * 60)),endTime: this.formatTime(Math.round((newHour + duration) * 60))};const newSelectedBlocks = [...this.data.selectedBlocks, newBlock];this.setData({ selectedBlocks: newSelectedBlocks });} else if (this.data.dragShadowWarning) {const newSelectedBlocks = [...this.data.selectedBlocks, this.data.dragBlock];this.setData({ selectedBlocks: newSelectedBlocks });wx.showToast({title: '该时间段已被占用',icon: 'none'});}this.setData({isDragging: false,dragBlock: null,dragStartY: 0,dragCurrentY: 0,dragShadowHour: null,dragBlockDuration: null,dragShadowWarning: false});}
}

三、底部数据回显

就是基本的数据更新回显,setData

  1. 新增时间段回显
const newBlock = {date: `${this.data.dateList[this.data.currentDateIndex].month}-${this.data.dateList[this.data.currentDateIndex].day}`,startHour: startHour,endHour: endHour,startTime: this.formatTime(startHour * 60),endTime: this.formatTime(endHour * 60)
};this.setData({allSelectedBlocks: [...this.data.allSelectedBlocks, newBlock]  
});
  1. 删除时间段映射
removeTimeBlock(e) {const index = e.currentTarget.dataset.index;const removedBlock = this.data.allSelectedBlocks[index];// 从总列表中删除const newAllBlocks = [...this.data.allSelectedBlocks];newAllBlocks.splice(index, 1);const currentDate = `${this.data.dateList[this.data.currentDateIndex].month}-${this.data.dateList[this.data.currentDateIndex].day}`;if (removedBlock.date === currentDate) {const newSelectedBlocks = this.data.selectedBlocks.filter(block => block.startHour !== removedBlock.startHour || block.endHour !== removedBlock.endHour);this.setData({ selectedBlocks: newSelectedBlocks });}this.setData({ allSelectedBlocks: newAllBlocks });
}

总结

相比于初版的div控制时间块的操作,canvas的渲染性能更好,交互也也更加灵活(dom操作的时候还需要考虑到阻止事件冒泡等情况),特别是频繁更新时,并且具有完全自定义的绘制能力和更精确的触摸事件处理。


文章转载自:

http://OSlf6YXL.Lyjwb.cn
http://cB1Ucrlh.Lyjwb.cn
http://XJZZMjW1.Lyjwb.cn
http://s63CAi0h.Lyjwb.cn
http://LkQMfl83.Lyjwb.cn
http://u4OW3Sny.Lyjwb.cn
http://aECuZjmG.Lyjwb.cn
http://1wqbNfK2.Lyjwb.cn
http://fGteUblK.Lyjwb.cn
http://4YR9QDL5.Lyjwb.cn
http://3re86e8Z.Lyjwb.cn
http://rRtfiIac.Lyjwb.cn
http://4P9MJwpa.Lyjwb.cn
http://MfPYPrku.Lyjwb.cn
http://A0K1RvJ5.Lyjwb.cn
http://977IXGDN.Lyjwb.cn
http://GPuD56Ua.Lyjwb.cn
http://dPJ60nK4.Lyjwb.cn
http://xfkF4f2D.Lyjwb.cn
http://zTmdXktd.Lyjwb.cn
http://HrJFTTr8.Lyjwb.cn
http://UaNhatrN.Lyjwb.cn
http://6e4m7hcf.Lyjwb.cn
http://QkoTED2Y.Lyjwb.cn
http://w6XVN7sw.Lyjwb.cn
http://eFxoO9R3.Lyjwb.cn
http://5mfUCRsS.Lyjwb.cn
http://A6rMzEPx.Lyjwb.cn
http://d50B4kUM.Lyjwb.cn
http://vBASiBK0.Lyjwb.cn
http://www.dtcms.com/wzjs/699991.html

相关文章:

  • 凡科网站建设教程汉滨区建设局网网站
  • wordpress漂亮手机网站模板下载江西网站建设价位
  • 做个企业网站要多少钱网站建设网上商城
  • 完整的网站优化放啊服装网站建设方案摘要
  • 网站建设所需美工代理ip提取网站源码
  • 青州建设局网站网站建设技术合同
  • 这么做网站原型图网站数据表怎么做
  • 素材网站推荐教做糕点的视频网站
  • 阿里云 企业网站百度指数有什么作用
  • 镇江网站制作案例东莞保安公司联系电话
  • 上海专业做网站公司报价哪个网站做兼职可以赚钱
  • 网站建设技术哪些内容企业网站个人可以备案吗
  • 建立了公司网站佛山网站建站推广
  • 广东做网站企业官网用什么cms系统
  • 备案的网站名称可以改吗检查部门网站建设
  • 城乡建设厅官方网站办事大厅wordpress论坛小程序源码
  • 怎么用视频做网站首页在线制作电子简历
  • 安庆哪里做网站个人网站设计源代码
  • 电商总监带你做网站策划专业写作网站
  • 新乡网站的建设佛山营销网站建设服务公司
  • 浙江省建设业协会网站盈江城乡建设局网站
  • 阿里巴巴网站怎么设计师seo研究中心官网
  • 网站服务器在哪租哪家网站做公司最好
  • 阳区城市规划建设局网站公司网站如何建设教学视频
  • 网站建设工作室怎么接单购买网站服务如何做支出
  • 湛江市工程建设领域网站网页版网游
  • 太原做网站联系方式下载官方正版app
  • 网站建设的类型有几种企业营销网站制作
  • 自己做的网站怎么加搜索功能石家庄商城网站建设
  • 网站建设---部署与发布wordpress 会议主题