自定义HT for Web之HTML5表格组件的Renderer和Editor

2024-08-30 11:58

本文主要是介绍自定义HT for Web之HTML5表格组件的Renderer和Editor,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在HT for Web提供了一下几种常用的Editor,分别是:

  • slider:拉条
  • color picker:颜色选择器
  • enum:枚举类型
  • boolean:真假编辑器
  • string:普通的文本编辑器

除了这几种常用编辑器之外,用户还可以通过继承ht.widget.BaseItemEditor类来实现自定义编辑器。

而渲染器,在HT for Web提供常用的Renderer有:

  • enum:枚举类型
  • color:颜色类型
  • boolean:真假渲染器
  • text:文本渲染器

和编辑器一样也可以自定义渲染器,但是方式不太一样,渲染器是通过定义column中drawCell()方法来自定义单元格展现效果。

今天我们就来实现一把自定义HTML5表格组件的Renderer和Editor,为了更直观地演示编辑效果,我们正好利用HT for Web强大的HTML5拓扑图组件

首先来瞧瞧效果:

 


 

效果图中,左边表格的第二列,是定义了一个编辑器,用一个圆盘来表示当前文本的旋转角度,可以通过拖拉来实现角度变换;表格的第三列,是通过drawCell()方法来绘制单元格内容,中间线标识旋转角度为零,向左表示文本逆时针旋转指定角度,向右表示文本顺时针旋转指定角度。

HT for Web的拓扑图网络节点的文字,简单修改label.rotation属性即可实现文字旋转功能,为了更直观我特意加上label.background使得网络拓扑图节点文字具有背景效果。

接下来我们就来看看具体的实现,先来了解下渲染器的实现:

 

 

{name : 'label.rotation',accessType : 'style',drawCell : function(g, data, selected, column, x, y, w, h, tableView) {var degree = Math.round(data.s('label.rotation') / Math.PI * 180),width = Math.abs(w / 360 * degree),begin = w / 2,rectColor = '#29BB9C',fontColor = '#000',background = '#F8F0E5';if (selected) {rectColor = '#F7F283';background = '#29BB9C';}g.beginPath();g.fillStyle = background;g.fillRect(x, y, w, h);g.beginPath();if (degree < 0) begin -= width;g.fillStyle = rectColor;g.fillRect(x + begin, y, width, h);g.beginPath();g.font = '12px arial, sans-serif';g.fillStyle = fontColor;g.textAlign = 'center';g.textBaseline = 'middle';g.fillText(degree, x + w / 2, y + h / 2);}
}

 

 

上面的代码就是定义表格第三列的代码,可以看到除了定义column自身属性外,还添加了drawCell()方法,通过drawCell()方法传递进来的参数,来绘制自己想要的效果。

 

渲染就是这么简单,那么编辑器就没那么容易了,在设计自定义编辑器之前,得先来了解下编辑器的基类ht.widget.BaseItemEditor,其代码如下:

 

ht.widget.BaseItemEditor = function (data, column, master, editInfo) {    this._data = data;this._column = column;this._master = master;this._editInfo = editInfo;
};
ht.Default.def(‘ht.widget.BaseItemEditor’, Object, {ms_ac:["data", "column", "master", "editInfo"],editBeginning: function() {},getView: function() {},getValue: function() {},setValue: function() {}
});

 

它处理构造函数中初始化类变量外,就定义了几个接口,让用户重载实现相关业务操作逻辑处理。那么接下来说说这些接口的具体用意:

editBeginning:在单元格开始编辑前调用

  • getView:获取编辑器view,值类型为DOM元素
  • getValue:获取编辑器值
  • setValue:设置编辑器值,并做编辑器的页面初始化操作

在创建一个自定义编辑器的时候,必须实现这些接口,并在不同的接口中,做不同的操作。

现在我们来看看旋转角度的自定义编辑是如何设计的:

    1. 按照HT for Web组件的设计惯例,我们需要创建一个Div作为view,在view中包含一个canvas元素,组件内容在canvas上绘制;

    2. editor需要与用户有交互,因此,需要在view上添加事件监听,监听用户有可能的操作,在这次的Demo中,我们希望用户通过拖拉角度控制盘来控制角度,所以,我们在view上添加了mousedown、mousemove及mouseup三个事件监听;

    3. 用户通过拖拉组件可以改变角度,这个改变是连续的,而且在拖拉的时候有可能鼠标会离开组件区域,要实现离开组件区域也能够正确的改变值,那么这时候就需要调用HT for Web的startDragging()方法;

 

以上讲述的操作都在构造函数中处理,接下来看看构造函数长什么样:

 

// 类ht.widget.RotationEditor构造函数
ht.widget.RotationEditor = function(data, column, master, editInfo) {// 调用父类构造函数初始化参数this.getSuperClass().call(this, data, column, master, editInfo);var self = this,view = self._view = createDiv(1),canvas = self._canvas = createCanvas(self._view);view.style.boxShadow = '2px 2px 10px #000';// 在view上添加mousemove监听view.addEventListener('mousemove', function(e) {if (self._state) {ht.Default.startDragging(self, e);}});// 在view上添加mousedown监听view.addEventListener('mousedown', function(e) {self._state = 1;self.handleWindowMouseMove(e);});// 在view上添加mouseup监听,做些清理操作view.addEventListener('mouseup', function(e) {self.clear();});
};

 

    4. 接下来就是通过def()方法来定义ht.widget.RotationEditor类继承于ht.widget.BaseItemEditor,并实现父类的方法,代码如下,在代码中,我没有贴出setValue()方法的实现,因为这块有些复杂,我们单独抽出来讲解;

 

ht.Default.def('ht.widget.RotationEditor', ht.widget.BaseItemEditor, {editBeginning : function() {var self = this,editInfo = self.getEditInfo(),rect = editInfo.rect;// 编辑前再对组件做一次布局,避免组件宽高计算不到位layout(self, rect.x, rect.y, rect.width, rect.width);},getView : function() {return this._view;},getValue : function() {return this._value;},setValue : function(val) {// 设置编辑器值,并做编辑器的页面初始化操作}
});

 

    5. 我们要在setValue()方法中绘制出文章开头的效果图上面展现的效果,大致分解了些,可以分成以下四步来绘制,当然在绘制之前需要线获得canvas的context对象:

        5.1. 绘制内外圆盘,通过arc()方法绘制两个间隔10px的同心圆;

        5.2. 绘制值区域,通过结合arc()方法及lineTo()方法绘制一个扇形区域,在通过fill方法填充颜色;

        5.3. 绘制指针,通过lineTo()方法绘制两个指针;

        5.4. 绘制文本,在绘制文本的时候,不能直接将文本绘制在圆心处,因为圆心处是指针的交汇处,如果直接绘制文本的话,将与指针重叠,这时,通过clearRect()方法来清除文本区域,在通过fillRect()方法将背景填充上去,不然文本区域块将是透明的,接下来就调用fillText()方法绘制文本。

 

这些就是组件绘制的所有逻辑,但是有一点必须注意,在绘制完组件后,必须调用下restore()方法,因为在initContext()方法中做了一次save()操作,接下来看看具体实现(代码有些长);

 

setValue : function(val) {var self = this;if (self._value === val) return;// 设置组件值self._value = val;var editInfo = self.getEditInfo(),rect = editInfo.rect,canvas = self._canvas,radius = self._radius = rect.width / 2,det = 10,border = 2,x = radius,y = radius;// 弧度到角度的转换val = Math.round(val / Math.PI * 180);// 设置canvas大小setCanvas(canvas, rect.width, rect.width);// 获取画笔var g = initContext(canvas);translateAndScale(g, 0, 0, 1);// 绘制背景g.fillStyle = '#FFF';g.fillRect(0, 0, radius * 2, radius * 2);// 设置线条颜色及线条宽度g.strokeStyle = '#969698';g.lineWidth = border;// 绘制外圈g.beginPath();g.arc(x, y, radius - border, 0, Math.PI * 2, true);g.stroke();// 绘制内圈g.beginPath();g.arc(x, y, radius - det - border, 0, Math.PI * 2, true);g.stroke();// 绘制值区域var start = -Math.PI / 2,end = Math.PI * val / 180 - Math.PI / 2;g.beginPath();g.fillStyle = 'rgba(255, 0, 0, 0.7)';g.arc(x, y, radius - border, end, start, !(val < 0));g.lineTo(x, border + det);g.arc(x, y, radius - det - border, start, end, val < 0);g.closePath();// 填充值区域g.fill();// 绘制值区域末端到圆心的线条g.lineTo(x, y);g.lineTo(x, det + border);g.stroke();// 绘制文本var font = '12px arial, sans-serif';// 计算文本大小var textSize = ht.Default.getTextSize(font, '-180');// 文本区域var textRect = {x : x - textSize.width / 2,y : y - textSize.height / 2,width : textSize.width,height : textSize.height};g.beginPath();// 清空文本区域g.clearRect(textRect.x, textRect.y, textRect.width, textRect.height);g.fillStyle = '#FFF';// 补上背景g.fillRect(textRect.x, textRect.y, textRect.width, textRect.height);// 设置文本样式g.textAlign = 'center';g.textBaseline = 'middle';g.font = font;g.fillStyle = 'black';// 绘制文本g.fillText(val, x, y);// restore()和save()是配对的,在initContext()方法中已经做了save()操作g.restore();
}

 

    6. 这时候编辑器的设计就大体完成,那么编辑器该如何用到表格上呢?很简单,在表格定义列的时候,加上下面两行代码就可以开始使用编辑器了;

 

editable : true, // 启动编辑
itemEditor : ‘ht.widget.RotationEditor' // 指点编辑器类

 

介绍到这里,编辑器可以正常的绘制出来,但是在操作的时候,你会发现,编辑器并不会根据拖拉的位置而改变角度,这是为什么呢?请看下一点说明:

 

    7. 在构造函数中,view的mousemove事件调用了startDragging()方法,其实这个方法是有依赖的,它需要组件重载handleWindowMouseMove()及handleWindowMouseUp()两个方法。原因很简单,就如第3点种提到的,用户在拖拉组件的时候,有可能拖离了组件区域,这时候只能通过window上的mousemove及mouseup两个事件监听令用户继续操作;

 

// 监听window的mousemove事件,在view的mousemove事件中,调用了startDragging()方法,
// 而startDragging()方法中的实质就是触发window的mousemove事件
// 该方法计算值的变化,并通过setValue()方法来改变值
handleWindowMouseMove : function(e) {var rect = this._view.getBoundingClientRect(),x = e.x - rect.left,y = e.y - rect.top,radius = this._radius,// 通过反三角函数计算弧度,再将弧度转换为角度value = Math.round(Math.atan2(y - radius, x - radius) / Math.PI * 180);if (value > 90) {value = -(180 - value + 90);}else {value = value + 90;}this.setValue(value / 180 * Math.PI);
},
handleWindowMouseUp : function(e) {this.clear();
},
clear : function() {// 清楚状态组件状态delete this._state;
}

 

加上上面的三个方法,运行代码可以发现编辑器可以正常编辑了。但是只有在结束编辑后,才可以在拓扑图上看到文本旋转角度变化,如果可以实时更新拓扑图上的文本旋转角度,将会更加直观些,那么现在该怎么办呢?

 

   8.自定义编辑器这块并像其他已经实现了的编辑器那样可以指定编辑器的属性,自定义编辑器能够指定的就只有一个类名,所以在编辑器上设置参数是没用的,用户无法设置到编辑器中。一个偷巧的方法是在column上做手脚,借鉴其他编辑器的设计思想,在column上添加一个名字为_instant的属性,在代码中通过该属性值来判断是否要立即更新对应的属性值,因此只需要在setValue()方法中添加如下代码,就能够实现实时更新属性值的效果;

 

// 判断列对象是否设置了_instant属性
if (column._instant) {var table = self.getMaster();table.setValue(self.getData(), column, val);
}

 

   9.至此,编辑器的设计已经完成,现在来看看具体的用法,下面的代码是Table中具体的列定义,在列定义中,指定itemEditor属性值,并设置_instant属性为true,就可以实现编辑器实时更新的效果

 

{accessType : 'style',name : 'label.rotation',editable : true,itemEditor : 'ht.widget.RotationEditor',_instant : true,formatValue : function(value) {return Math.round(value / Math.PI * 180);}
}

 

代码中你会发现定义了一个formatValue()方法,该方法是为了与编辑器中编辑的值类型一致,都将弧度转换为角度。

在表格的第三列中,通过渲染器自定义了单元格样式,同时我也为其定义了另外一个编辑器,通过左右拖拉单元格来实现角度的变化,这个编辑器的实现与上面谈及的编辑器略有不同,具体的不同之处在于,第三列的编辑器通过HT for Web中定义的ms_listener模块来添加监听,让构造函数与交互分离开,看起来更加清晰明了。

介绍下ms_listener模块,如果类添加了ms_listener模块,那么在类中将会多以下两个方法:

  • addListeners:将类中定义的handle_XXX()方法(XXX代表某个DOM事件名称,如:mousemove等)作为相应的事件监听函数添加到组件的view上;
  • removeListeners:将类中定义的handle_XXX()方法对应的事件从view上移除。

那么类中如何添加ms_listener模块呢,只需要在def()方法中类的方法定义上,添加ms_listener:true这行代码,并在方法定义上添加DOM事件对应的handle函数,再在构造函数中调用类的addListeners()方法。

具体的代码我就不在阐述了,思路与前面讲述的编辑器的思路差不多。

 

最后附上程序的所有代码,供大家参考,有什么问题欢迎留言咨询。

 

这篇关于自定义HT for Web之HTML5表格组件的Renderer和Editor的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1120758

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

Java Web指的是什么

Java Web指的是使用Java技术进行Web开发的一种方式。Java在Web开发领域有着广泛的应用,主要通过Java EE(Enterprise Edition)平台来实现。  主要特点和技术包括: 1. Servlets和JSP:     Servlets 是Java编写的服务器端程序,用于处理客户端请求和生成动态网页内容。     JSP(JavaServer Pages)

BUUCTF靶场[web][极客大挑战 2019]Http、[HCTF 2018]admin

目录   [web][极客大挑战 2019]Http 考点:Referer协议、UA协议、X-Forwarded-For协议 [web][HCTF 2018]admin 考点:弱密码字典爆破 四种方法:   [web][极客大挑战 2019]Http 考点:Referer协议、UA协议、X-Forwarded-For协议 访问环境 老规矩,我们先查看源代码

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

Vue3项目开发——新闻发布管理系统(六)

文章目录 八、首页设计开发1、页面设计2、登录访问拦截实现3、用户基本信息显示①封装用户基本信息获取接口②用户基本信息存储③用户基本信息调用④用户基本信息动态渲染 4、退出功能实现①注册点击事件②添加退出功能③数据清理 5、代码下载 八、首页设计开发 登录成功后,系统就进入了首页。接下来,也就进行首页的开发了。 1、页面设计 系统页面主要分为三部分,左侧为系统的菜单栏,右侧