您现在的位置是:网站首页> 编程资料编程资料
HTML5 Canvas 实现K线图的示例代码html5实现点击弹出图片功能html5 录制mp3音频支持采样率和比特率设置html5表单的required属性使用html5调用摄像头实例代码HTML5页面音频自动播放的实现方式Html5大屏数据可视化开发的实现html实现弹窗的实例HTML5来实现本地文件读取和写入的实现方法HTML 罗盘式时钟的实现HTML5简单实现添加背景音乐的几种方法
2023-10-12
336人已围观
简介 这篇文章主要介绍了HTML5 Canvas 实现K线图的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
因为公司的项目需求,需要做一个K线图,可以让交易者清楚的看到某一交易品种在各个时间段内的报价,以及当前的实时报价。
我所考虑的有两个方向,一是类似于Highcharts等插件的实现方式 -- svg,一是HTML5的canvas。
SVG 是一种使用 XML 描述 2D 图形的语言。 Canvas 通过 JavaScript 来绘制 2D 图形。 Canvas 是逐像素进行渲染的。
'
经过上面的比较不难发现, SVG 更适用于偏静态,渲染频率不高的场景,所以这种要实现实时报价更新绘制的情况只能选择 canvas。
2. 实现哪些需求
历史报价 和 实时报价 绘制图表
支持 拖拽 查看历史时间段的报价图表
支持鼠标 滚轮 和触摸板 双指 操作放大或缩小图表
支持鼠标指针 移动 查看鼠标位置报价
3. 代码实现过程
1. 准备工作
/** * K-line - K线图渲染函数 * Date: 2019.12.18 Author: isnan */ const BLOCK_MARGIN = 2; //方块水平间距 const START_PRICE_INDEX = 'open_price'; //开始价格在数据组中的位置 const END_PRICE_INDEX = 'close'; //结束价格在数据组中的位置 const MIN_PRICE_INDEX = 'low'; //最小价格在数据组中的位置 const MAX_PRICE_INDEX = 'high'; //最大价格在数据组中的位置 const TIME_INDEX = 'time'; //时间在数据组中的位置 const LINE_WIDTH = 1; //1px 宽度 (中间线、x轴等) const BOTTOM_SPACE = 40; //底部空间 const TOP_SPACE = 20; //顶部空间 const RIGHT_SPACE = 60; //右侧空间 let _addEventListener, _removeEventListener, prefix = ''; //addEventListener 浏览器兼容 function RenderKLine (id, /*Optional*/options) { if (!id) return; options = options || {}; this.id = id; //canvas box id // detect event model if (window.addEventListener) { _addEventListener = "addEventListener"; _removeEventListener = "removeEventListener"; } else { _addEventListener = "attachEvent"; _removeEventListener = "detachEvent" prefix = "on"; } // options params this.sharpness = options.sharpness; // 清晰度 (正整数 太大可能会卡顿,取决于电脑配置 建议在2~5区间) this.blockWidth = options.blockWidth; // 方块的宽度 (最小为3,最大49 为了防止中间线出现位置偏差 设定为奇数,若为偶数则向下减1) this.buyColor = options.buyColor || '#F05452'; // color 涨 this.sellColor = options.sellColor || '#25C875'; // color 跌 this.fontColor = options.fontColor || '#666666'; //文字颜色 this.lineColor = options.lineColor || '#DDDDDD'; //参考线颜色 this.digitsPoint = options.digitsPoint || 2; //报价的digits (有几位小数) this.horizontalCells = options.horizontalCells || 5; //水平方向切割多少格子 (中间虚线数 = 5 - 1) this.crossLineStatus = options.crossLineStatus || true; //鼠标移动十字线显示状态 //basic params this.totalWidth = 0; //总宽度 this.movingRange = 0; //横向移动的距离 取正数值,使用时再加负号 this.minPrice = 9999999; this.maxPrice = 0; //绘制的所有数据中 最小/最大数据 用来绘制y轴 this.diffPrice = 0; //最大报价与最小报价的差值 this.perPricePixel = 0; //每一个单位报价占用多少像素 this.centerSpace = 0; //x轴到顶部的距离 绘图区域 this.xDateSpace = 6; //x轴上的时间绘制间隔多少组 this.fromSpaceNum = 0; //x轴上的时间绘制从第 (fromSpaceNum%xDateSpace) 组数据开始 this.dataArr = []; //数据 this.lastDataTimestamp = undefined; //历史报价中第一个时间戳, 用来和实时报价做比较画图 this.buyColorRGB = {r: 0, g: 0, b: 0}; this.sellColorRGB = {r: 0, g: 0, b: 0}; this.processParams(); this.init(); }
定义了一些常量和变量,生成一个 构造函数 ,接收两个参数,一个是id,canvas会在插入到这个id的盒子内,第二个参数是一些配置项,可选。
/** * sharpness {number} 清晰度 * buyColor {string} color - 涨 * sellColor {string} color - 跌 * fontColor {string} 文字颜色 * lineColor {string} 参考线颜色 * blockWidth {number} 方块的宽度 * digitsPoint {number} 报价有几位小数 * horizontalCells {number} 水平方向切割几个格子 * crossLineStatus {boolean} 鼠标移动十字线显示状态 */
2. init方法和canvas画布的翻转
RenderKLine.prototype.init = function () { let cBox = document.getElementById(this.id); // 创建canvas并获得canvas上下文 this.canvas = document.createElement("canvas"); if (this.canvas && this.canvas.getContext) { this.ctx = this.canvas.getContext("2d"); } this.canvas.innerHTML = '您的当前浏览器不支持HTML5 canvas'; cBox.appendChild(this.canvas); this.actualWidth = cBox.clientWidth; this.actualHeight = cBox.clientHeight; this.enlargeCanvas(); } // 因为绘制区域超出canvas区域,此方法也用来代替clearRect 清空画布的作用 RenderKLine.prototype.enlargeCanvas = function () { this.canvas.width = this.actualWidth * this.sharpness; this.canvas.height = this.actualHeight * this.sharpness; this.canvas.style.height = this.canvas.height / this.sharpness + 'px'; this.canvas.style.width = this.canvas.width / this.sharpness + 'px'; this.centerSpace = this.canvas.height - (BOTTOM_SPACE + TOP_SPACE) * this.sharpness; // 将canvas原点坐标转换到右上角 this.transformOrigin(); // base settings this.ctx.lineWidth = LINE_WIDTH*this.sharpness; this.ctx.font = `${12*this.sharpness}px Arial`; // 还原之前滚动的距离 this.ctx.translate(-this.movingRange * this.sharpness, 0); // console.log(this.movingRange); }
init方法初始化了一个canvas,enlargeCanvas是一个替代clearRect的方法,其中需要注意的是 transformOrigin 这个方法,因为正常的canvas原点坐标在坐上角,但是我们需要绘制的图像是从右侧开始绘制的,所以我这里为了方便绘图,把整个canvas做了一次转换,原点坐标转到了右上角位置。
// 切换坐标系走向 (原点在左上角 or 右上角) RenderKLine.prototype.transformOrigin = function () { this.ctx.translate(this.canvas.width, 0); this.ctx.scale(-1, 1); }
这里有一点需要注意的是,虽然翻转过来绘制一些矩形,直线没什么问题,但是绘制文本是不行的,绘制文本需要还原回去,不然文字就是翻转过来的状态。如下图所示:
3. 移动、拖拽、滚轮事件
//监听鼠标移动 RenderKLine.prototype.addMouseMove = function () { this.canvas[_addEventListener](prefix+"mousemove", mosueMoveEvent); this.canvas[_addEventListener](prefix+"mouseleave", e => { this.event = undefined; this.enlargeCanvas(); this.updateData(); }); const _this = this; function mosueMoveEvent (e) { if (!_this.dataArr.length) return; _this.event = e || event; _this.enlargeCanvas(); _this.updateData(); } } //拖拽事件 RenderKLine.prototype.addMouseDrag = function () { let pageX, moveX = 0; this.canvas[_addEventListener](prefix+'mousedown', e => { e = e || event; pageX = e.pageX; this.canvas[_addEventListener](prefix+'mousemove', dragMouseMoveEvent); }); this.canvas[_addEventListener](prefix+'mouseup', e => { this.canvas[_removeEventListener](prefix+'mousemove', dragMouseMoveEvent); }); this.canvas[_addEventListener](prefix+'mouseleave', e => { this.canvas[_removeEventListener](prefix+'mousemove', dragMouseMoveEvent); }); const _this = this; function dragMouseMoveEvent (e) { if (!_this.dataArr.length) return; e = e || event; moveX = e.pageX - pageX; pageX = e.pageX; _this.translateKLine(moveX); // console.log(moveX); } } //Mac双指行为 & 鼠标滚轮 RenderKLine.prototype.addMouseWheel = function () { addWheelListener(this.canvas, wheelEvent); const _this = this; function wheelEvent (e) { if (Math.abs(e.deltaX) !== 0 && Math.abs(e.deltaY) !== 0) return; //没有固定方向,忽略 if (e.deltaX < 0) return _this.translateKLine(parseInt(-e.deltaX)); //向右 if (e.deltaX > 0) return _this.translateKLine(parseInt(-e.deltaX)); //向左 if (e.ctrlKey) { if (e.deltaY > 0) return _this.scaleKLine(-1); //向内 if (e.deltaY < 0) return _this.scaleKLine(1); //向外 } else { if (e.deltaY > 0) return _this.scaleKLine(1); //向上 if (e.deltaY < 0) return _this.scaleKLine(-1); //向下 } } }
滚轮事件上一篇已经说过了,这里就是对不同情况做相应的处理;
鼠标移动事件 把event更新到 this 上,然后调用 updateData 方法,绘制图像即可。会调用下面方法画出十字线。
function drawCrossLine () { if (!this.crossLineStatus || !this.event) return; let cRect = this.canvas.getBoundingClientRect(); //layerX 有兼容性问题,使用clientX let x = this.canvas.width - (this.event.clientX - cRect.left - this.movingRange) * this.sharpness; let y = (this.event.clientY - cRect.top) * this.sharpness; // 在报价范围内画线 if (y < TOP_SPACE*this.sharpness || y > this.canvas.height - BOTTOM_SPACE * this.sharpness) return; this.drawDash(this.movingRange * this.sharpness, y, this.canvas.width+this.movingRange * this.sharpness, y, '#999999'); this.drawDash(x, TOP_SPACE*this.sharpness, x, this.canvas.height - BOTTOM_SPACE*this.sharpness, '#999999'); //报价 this.ctx.save(); this.ctx.translate(this.movingRange * this.sharpness, 0); // 填充文字时需要把canvas的转换还原回来,防止文字翻转变形 let str = (this.maxPrice - (y - TOP_SPACE * this.sharpness) / this.perPricePixel).toFixed(this.digitsPoint); this.transformOrigin(); this.ctx.translate(this.canvas.width - RIGHT_SPACE * this.sharpness, 0); this.drawRect(-3*this.sharpness, y-10*this.sharpness, this.ctx.measureText(str).width+6*this.sharpness, 20*this.sharpness, "#ccc"); this.drawText(str, 0, y, RIGHT_SPACE * this.sharpness) this.ctx.restore(); }
拖拽事件 把 pageX 的移动距离传递给 translateKLine 方法来实现横向滚动查看。
/** * 缩放图表 * @param {int} scaleTimes 缩放倍数 * 正数为放大,负数为缩小,数值*2 代表蜡烛图width的变化度 * eg: 2 >> this.blockWidth + 2*2 * -3 >> this.blockWidth - 3*2 * 为了保证缩放的效果, * 应该以当前可视区域的中心为基准缩放 * 所以缩放前后两边的长度在总长度中所占比例应该一样 * 公式:(oldRange+0.5*canvasWidth)/oldTotalLen = (newRange+0.5*canvasWidth)/newTotalLen * diffRange = newRange - oldRange * = (oldRange*newTotalLen + 0.5*canvasWidth*newTotalLen - 0.5*canvasWidth*oldTotalLen)/oldTotalLen - oldRange */ RenderKLine.prototype.scaleKLine = function (scaleTimes) { if (!this.dataArr.length) return; let oldTotalLen = this.totalWidth; this.blockWidth += scaleTimes*2; this.processParams(); this.computeTotalWidth(); let newRange = (this.movingRange*this.sharpness*this.totalWidth+this.canvas.width/2*this.totalWidth-this.canvas.width/2*oldTotalLen)/oldTotalLen/this.sharpness; let diffRange = newRange - this.movingRange; // console.log(newRange, this.movingRange, diffRange); this.translateKLine(diffRange); } // 移动图表 RenderKLine.prototype.translateKLine = function (range) { if (!this.dataArr.length) return; this.movingRange += parseInt(range); let maxMovingRange = (this.totalWidth - this.canvas.width) / this.sharpness + this.blockWidth; if (this.totalWidth <= this.canvas.width || this.movingRange <= 0) { this.movingRange = 0; } else if (this.movingRange >=
相关内容
- html5利用canvas实现颜色容差抠图功能canvas像素点操作之视频绿幕抠图
- HTML5 客户端数据库简易使用:IndexedDBHTML5本地存储之IndexedDBHTML5中indexedDB 数据库的使用实例html5 初试 indexedDB(推荐)深入解析HTML5的IndexedDB索引数据库
- Html5 Canvas实现图片标记、缩放、移动和保存历史状态功能 (附转换公式)详解如何用HTML5 Canvas API控制图片的缩放变换HTML5 Canvas实现图片缩放、翻转、颜色渐变的代码示例通过Canvas及File API缩放并上传图片完整示例
- Html+Css+Jquery实现左侧滑动拉伸导航菜单栏的示例代码如何通过 display:olck/none 完成一个菜单栏使用layui实现左侧菜单栏及动态操作tab项的方法使用HTML+CSS实现鼠标划过的二级菜单栏的示例详解css3 Transition属性(平滑过渡菜单栏案例)菜单栏 “三” 变形为“X”css3过渡动画利用CSS实现几款不错的菜单栏实例代码CSS仿网易首页的头部菜单栏按钮和三角形制作方法纯CSS制作菜单栏当鼠标经过时会变色的利用html+css实现菜单栏缓慢下拉效果的示例代码
- canvas实现手机的手势解锁的步骤详细 html5 canvas手势解锁源码分享h5使用canvas画布实现手势解锁
- 100floors电梯 第46层 图文攻略_手机游戏_游戏攻略_
- 100floors电梯 第47层 图文攻略_手机游戏_游戏攻略_
- 100floors电梯 第48层 图文攻略_手机游戏_游戏攻略_
- 100floors电梯 第49层 图文攻略_手机游戏_游戏攻略_
- 100floors电梯 第50层 图文攻略_手机游戏_游戏攻略_
点击排行
本栏推荐
