背景
最近、SCADA プロジェクトの Web ページに機器レポートを表示する必要性が生じました。完全なレポートには通常、フィルター操作領域、テーブル、グラフ、表示ボードなどのさまざまな要素が含まれており、その中で最も一般的に使用されるコントロールはデータ テーブルです。以前の産業プロジェクトでは、すべてのテーブルが同じように見え、数字と単純な背景色の変更によって関連情報が表示されていました。しかし現在、さまざまなモバイル アプリや Web アプリケーションの影響により、特に Web プロジェクトにおいて、人々の美的感覚や要件は常に向上しており、昔ながらのデジタル テーブルを依然として使用するのは確かに少し時代遅れです。
適切な HTML フロントエンド テーブル コントロールを選択するにはどうすればよいですか?ここでは一万語は省略できます。ははは。 jQuery、Angular、React などのコントロール ライブラリには成熟したケースが多数ありますが、これらの DOM ベースのコントロールにも欠点があります。1 つは、大量のデータを含むテーブルでカスタム セル コントロールを使用する場合です。データ、特にモバイルではブラウザの負担が大きすぎます。もう 1 つの問題は、開発効率です。上記のコントロール ライブラリはカプセル化の度合いやインターフェイスの形式が異なりますが、全体として開発者は CSS と JS についての深い理解が必要です。コントロールの再利用、埋め込み、公開、移植にも問題があります。
上記の検討に基づいて、最終的に Canvas ベースの HT を採用しました。 HT テーブル コントロールのカスタム レンダリング インターフェイスと Web ワーカーのマルチスレッド データ シミュレーションを通じて、テーブル コントロールの効果は次のようになります:
開始
最初に行う必要があるのは、ビジネス ロジックを使用して、異なる方法で表示されたテーブル データのさまざまな列を比較します。例えば、機器の履歴情報のうち、稼働時間やダウンタイムなどを円グラフにまとめて表示すると、一覧から直感的に機器の状態を比較することができます。このステップがどのように実行されるかを見てみましょう。
HT には独自の DataModel データ モデルがあり、データ ステータスの管理、タイム ディスパッチ、UI の更新に関する開発作業が省略されます。 DataModel コンテナーの子要素 Data は、HT の最も基本的なデータ構造であり、さまざまな UI コントロールにマップできます。キャンバスでは、データをベクター、画像、テキストなどとして表示できます。ツリー コントロールでは、データをツリーのノードとして表示できます。テーブル内の各データはテーブル内の行に対応します。つまり、テーブル コントロール自体に DataModel が含まれており、描画時に、この Model 内の各データが行に描画されます。異なる列には、データの異なる属性が表示されます。たとえば、デバイスのダウンタイムを Data の stop プロパティに保存できます。テーブルの列情報を構成するときに、列「ダウンタイム」のヘッダーの説明、データの Stopping プロパティに対応するデータ セル、およびセル内のカスタム描画形式:
{ name: 'stopping', //对应的data属性 accessType: 'attr', align: 'center', color: '#E2E2E2', //文字颜色 displayName: '停机', //表头描述 drawCell: pageTable.getDrawLegend('stopping','#E2E2E2') },
カスタム レンダリング
を指定できます。基本的な表示形式、テキスト、配列、色などの型がデフォルトで提供されており、データを自動的に整形してテキストや背景色などで表示できますが、まだ個別のニーズを満たしていないため、列を変更するために必要です。drawCell はカスタム レンダリング関数としてオーバーロードされます。 drawCellのパラメータ: function (g, data, selected, columns, x, y, w, h, view)、gはCanvasの環境情報、dataは行のデータ本体 この情報をもとに、 HTML5 ネイティブを使用します。 Canvas API を使用すると、目的の効果を描画できます。
HTML5 のネイティブ インターフェイスを直接使用するのが面倒ですか? HT は、さまざまなベクター タイプやいくつかの単純なチャートを含む、Canvas API のカプセル化インターフェイスを提供します。この機能を使用すると、複雑な効果を簡単に組み合わせることができます。詳細については、ベクトルのマニュアルを参照してください
最初に、結合されたベクトルの説明情報を含むオブジェクトを作成し、次に画像オブジェクトを追加します。そして、drawCell のコンテキスト情報は、カスタム描画を実現するためのパラメーターとして ht.Default.drawStretchImage 関数に渡されます。
//drawCellfunction (g, data, selected, column, x, y, w, h, tableView) { var value = data.a(attr); var image = { width: 60, height: 30, comps: [ { type: 'rect', rect: [11,11,8,8], borderWidth: 1, borderColor: '#34495E', background: legendColor, depth: 3 }, { type: 'text', text: value, rect:[30, 0, 30, 30], align: 'left', color: '#eee', font: 'bold 12px Arial' } ]}; ht.Default.drawStretchImage(g, image, 'centerUniform', x, y, w, h); }
Legend 凡例には複数の列が表示されているため、単純にまとめて getDrawLegend 関数を使用できます。パラメータは列の凡例の色と Data 属性名で、戻り値はdrawCell 関数です。
getDrawLegend: function(attr,legendColor){return drawCell}
この時点で、開始時間列と終了時間列のカスタム描画が完了しました:
“统计”列的饼图,实际上更简单。还是利用 HT 的矢量接口,把上述几项时间数据传入饼图矢量结构即可。
var values = [ data.a('running'), data.a('stopping'), data.a('overhauling') ];var image = { width: 200, height: 200, comps: [ { type: 'pieChart', rect: [20,20, 150, 150], hollow: false, label: false, labelColor: 'white', shadow: true, shadowColor: 'rgba(0, 0, 0, 0.8)', values: values, startAngle: Math.PI, colors: pieColors } ] };
其他列的渲染过程大同小异。在“风速”列中,我们可以根据风速大小计算一个颜色透明值,来实现同一色系的映射变换,比原来那种非红即绿的报警表,看起来更舒服一些。在“可用率”列,用 Rect 的不同长度变化,来模拟进度条的效果。在功率曲线中稍微有点不同,因为想实现曲线覆盖区域的颜色渐变,在 HT 的 lineChart 中没有找到相关接口,所以直接采用了 Canvas 绘制。
为了运行效率考虑,在表格的单元格中绘制 Chart,应该追求简洁大方,一目了然。这几个 Legend 图例小矩形,其实是应该画在表头的。我为了偷懒,就画在了单元格,导致画面显得有点乱。
Web Worker
众所周知,浏览器的 JS 环境是基于单进程的,在页面元素较多,而且有很大运算需求的情况下,会导致无法兼顾渲染任务和计算任务,造成页面卡顿或失去响应。在这种情况,可以考虑使用 Web Worker 的多线程,来分担一些计算任务。
Web Worker 是 HTML5 的多线程 API,和我们原来传统概念中的多线程开发有所不同。Web Worker 的线程之间,没有内存共享的概念,所有信息交互都采用 Message 的异步传递。这样多线程之间无法访问对方的上下文,也无法访问对方的成员变量及函数,也不存在互斥锁等概念。在消息中传递的数据,也是通过值传递,而不是地址传递。
在 Demo 中,我们利用 Web Worker 作为模拟后端,产生虚拟数据。并采用前端分页的方式,从 worker 获取当前页显示条目的相关数据。 在主线程中,创建 Web Worker注册消息监听函数。
worker = new Worker("worker.js"); worker.addEventListener('message', function(e) { //收到worker的消息后,刷新表格 pageTable.update(e.data); }); pageTable.request = function(){ //向worker发送分页数据请求 worker.postMessage({ pageIndex: pageTable.getPageIndex(), pageRowSize: pageTable.getPageRowSize() }); }; pageTable.request();
本处的new Worker创建,对于主线程来说是异步的,等加载完 worker.js,并完成初始化后,该 worker 才是真正可用状态。我们不需要考虑 worker 的可用状态,可以在创建语句后直接发送消息。在完成初始化之前向其发送的请求,都会自动保存在主线程的临时消息队列中,等 worker 创建完成,这些信息会转移到 worker 的正式消息队列。
在 worker 中,创造虚拟随机数据,监听主线程消息,并返回其指定的数据。
self.addEventListener('message', function(e) { var pageInfo = getPageInfo(e.data.pageIndex, e.data.pageRowSize); self.postMessage(pageInfo); }, false);
由于前面提到的无法内存共享,Web Worker 无法操作 Dom,也不适用于与主线程进行大数据量频繁的交互。那么在生产环境中,Web Worker 能发挥什么作用?在我们这种应用场景,Web Worker 适合在后台进行数据清洗,可以对从后端取到的设备历史数据进行插值计算、格式转换等操作,再配合上 HT 的前端分页,就能实现大量数据的无压力展示。
分页
传统上有后端分页和前端分页,我们可以根据实际项目的数据量、网速、数据库等因素综合考虑。
采用后端分页的话,可以简化前端架构。缺点是换页时会有延迟,用户体验不好。而且在高并发的情况下,频繁的历史数据查询会对后端数据库造成很大压力。
采用前端分页,需要担心的是数据量。整表的数据量太大,会造成第一次获取时的加载太慢,前端资源占用过多。
在本项目中,得益于给力的 GOLDEN 实时数据库,我们可以放心的采用前端分页。历史数据插值、统计等操作可以在数据库层完成,传递到前端的是初步精简后的数据。在数千台设备的历史查询中,得到的数据量完全可以一次发送,再由前端分页展示。
在某些应用场景,我们会在表格中显示一些实时数据,这些数据是必须是动态获取的。类似在 Demo 中的趋势刷新效果,我们可以在创建表格时批量获取所有历史数据,然后再动态向数据库获取当前页所需的实时数据。如果网速实在不理想,也可以先只获取第一页的历史数据,随后在后台线程慢慢接收完整数据。
这样的架构实现了海量数据的快速加载,换页操作毫无延迟,当前页面元素实时动态刷新的最终效果。
还有一些传统客户,喜欢在一张完整的大表上进行数据筛选、排序等操作。
我们可以把 Demo 中的数据总量改成一万条,单页数量也是一万条,进行测试:
出乎意料的是,HT 面对上万数据量的复杂表格,轻松经受住了考验。页面的滚动、点击等交互毫无影响,动态刷新没有延迟,表格加载、排序等操作时,会有小的卡顿,在可接受的程度之内。当然也跟客户端的机器配置有关。可以想象,几万个 Chart的展示以及动态刷新,对于基于dom的控件几乎是件无法完成的任务。关于 HT 的其他矢量和控件,同样有高性能特性:
后记
如前文所述,我们基于 HT 的表格实现了海量数据的可定制展现,并取得了令人满意的效果。以下是一些还可以改进的地方。
在 Demo 中,通过对 HT 表格控件的 drawCell 进行重载,实现了自定义渲染,然后把这些 drawCell 放到了 PageTable 的原型函数中,以供 Column 调用。实际上,更好的办法应该是把这些常见的 Chart、图例封装到 Column 的基本类型中,那样在配置表格 Column 列时,可以指定 type 为 pieChart 或 lineChart 即可,不需再自行绘制相关矢量。
对于这些表格中的 Chart,也可以增加一些交互接口,例如可以增加单元格 Tooltip 的自定义渲染功能,在鼠标停留时浮出一个信息量更大的 Chart,可以对指定设备进行更深入的了解。
界面美观优化。对 HT 的控件进行颜色定制,可以通过相关接口进行配置:
var tableHeader = pageTable.getTablePane().getTableHeader(); tableHeader.getView().style.backgroundColor = 'rgba(51,51,51,1)'; tableHeader.setColumnLineColor('#777');var tableView = pageTable.getTablePane().getTableView(); tableView.setSelectBackground('#3D5D73'); tableView.setRowLineColor('#222941'); tableView.setColumnLineVisible(false); tableView.setRowHeight(30);
今后也可以对htconfig进行全局配置,在单独文件中进行样式的整体管理,实现外观样式与功能的分离,有助于工程管理。
以上がHTML5ベースのWeb SCADAレポートのグラフィックコードの詳細説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。