vue 使用TradingView制作K线图(模仿火币)详解

发布时间:2021-06-20 发表于话题:k线图上数字 1到9 点击:1570 当前位置:酷财经网 > 科技 > vue 使用TradingView制作K线图(模仿火币)详解 手机阅读

前言:
项目需求要写K线图,echarts图表实现不了里面的功能,所以要用到TradingView,这个真心让人头疼,百度的东西也很少,花了很久才实现出来了。
效果图:
k线是实时更新的,可以正常根据数据跳动

正式开始了
第一步:
index.html 引入js和css文件
我这里是vue-cli4 ,vue-cli4和vue-cli2文件夹不一样 如果你是vue-cli4 里面的css和js 记得放在public文件夹里面,vue-cli2的话就是正常使用

第二步:
先建立一个div,用来保存k线图

我的data里面的数据
(里面都是我用到的东西,懒得删了,用不到你可以删了)

widget: null, //创建实例 symbolInfo: null, feed: null, wsEx: null, ws: null, reals: null, lists: [], newData: "", symbol: "", priceScale: 100000, histime: "", socketTypr: false, bg_: "#fff", //背景颜色 isDay: "", grid: "#cbcbcb", //线条颜色 cross: "#cbcbcb"

第三步:
开始准备数据
this.symbol 是我获取的币种类型,如果你不需要获取直接在下面写死就可以了

第四步:
创建 createWidget() 函数 (它和正常函数一样是放在methods里面的)
里面的 symbol 要么是上面获取的,要么你自己写死
container_id 对应的id就是你创建的div的id

如果你是vue-cli4 的话 library_pathcustom_css_url 的路径必须要和我的一样,如果用相对路径会报错,vue-cli2的话就是正常使用

createWidget()完整代码

createWidget() { let _this = this; this.$nextTick(function () { let widget = (_this.widget = new TradingView.widget({ symbol: "BTC/USDT", interval: 1, debug: true, fullscreen: false, autosize: true, container_id: "tv_chart_container", // datafeed: new Datafeeds.UDFCompatibleDatafeed("http://demo_feed.tradingview.com"), datafeed: _this.createFeed(), library_path: "static/tradeview/charting_library/", custom_css_url: "bundles/new.css", locale: _this.setLanguage, width: "100%", height: 516, drawings_access: { type: "black", tools: [{ name: "Regression Trend" }], }, disabled_features: [ // 禁用的功能 "left_toolbar", "widget_logo", "header_saveload", "compare_symbol", "display_market_status", "go_to_date", "header_chart_type", "header_compare", "header_interval_dialog_button", "header_resolutions", "header_screenshot", "header_symbol_search", "header_undo_redo", "legend_context_menu", "show_hide_button_in_legend", "show_interval_dialog_on_key_press", "snapshot_trading_drawings", "symbol_info", "timeframes_toolbar", "use_localstorage_for_settings", "volume_force_overlay", ], enabled_features: [ // 启用的功能(备注:disable_resolution_rebuild 功能用于控制当时间范围为1个月时,日期刻度是否都是每个月1号 "dont_show_boolean_study_arguments", "hide_last_na_study_output", "move_logo_to_main_pane", "same_data_requery", "side_toolbar_in_fullscreen_mode", "disable_resolution_rebuild", ], charts_storage_url: "http://saveload.tradingview.com", charts_storage_api_version: "1.1", toolbar_bg: "transparent", timezone: "Asia/Shanghai", studies_overrides: { "volume.precision": "1000", }, overrides: _this.overrides(), })); widget.MAStudies = []; widget.selectedIntervalButton = null; // widget.setLanguage('en') widget.onChartReady(function () { //图表方法 // document.getElementById('trade-view').childNodes[0].setAttribute('style', 'display:block;width:100%;height:100%;'); //let that =this widget .chart() .createStudy( "Moving Average", false, true, [15, "close", 0], null, { "Plot.color": "#e843da", } ); // widget.chart().createStudy("MA Cross", false, false, [10, 20]); let buttonArr = [ { value: "1min", period: "1", text: "分时", chartType: 3, type: "min", }, { value: "1", period: "1m", text: "1分", chartType: 1, type: "1min", }, { value: "5", period: "5m", text: "5分", chartType: 1, type: "5min", }, // { // value: "15", // period: "15m", // text: "15分钟", // chartType: 1, // type:"15min" // }, { value: "30", period: "30m", text: "30分", chartType: 1, type: "30min", }, { value: "60", period: "60分", text: "1小时", chartType: 1, type: "60min", }, { value: "1D", period: "1D", text: "1天", chartType: 1, type: "1day", }, { value: "1W", period: "1W", text: "1周", chartType: 1, type: "1week", }, { value: "1M", period: "1M", text: "1月", chartType: 1, type: "1mon", }, ]; let btn = {}; let nowTime = ""; buttonArr.forEach((v, i) => { let button = widget.createButton(); button.attr("title", v.text).addClass("my2").text(v.text); if (v.text == "1分") { button.css({ color: "#5786d2", "border-bottom": "1px solid #5786d2", }); localStorage.setItem("tim", "1"); //默认为1分钟 localStorage.setItem("type", "1min"); //默认为1 } btn = button.on("click", function (e) { $(this) .parents(".left") .children() .find(".my2") .removeAttr("style"); //去掉1分钟的 handleClick(e, v.value, v.type); button.css({ color: "#5786d2", "border-bottom": "1px solid #5786d2", }); widget.chart().setChartType(v.chartType); //改变K线类型 }); }); let handleClick = (e, value, type) => { console.log(value); _this.setSymbol = function (symbol, value) { gh.chart().setSymbol(symbol, value); }; localStorage.setItem("tim", value); localStorage.setItem("type", type); widget.chart().setResolution(value, function onReadyCallback() {}); //改变分辨率 $(e.target) .addClass("mydate") .closest("div.space-single") .siblings("div.space-single") .find("div.button") .removeClass("mydate"); }; }); _this.widget = widget; }); }, }); },

buttonArr 是头部分离出来的导航,没有用自带的样式,你想写几种分类,就在这里面添加或者删除

然后循环buttonArr,开始给选中的导航增加css样式,这个可以自己定义
css在你引入的css文件里面添加,当前这个vue修改不了

第五步:
上面createWidget() 里面用到了createFeed() 方法,现在开始写createFeed() 方法

createFeed()完整代码

createFeed() { let this_vue = this; let Datafeed = {}; Datafeed.DataPulseUpdater = function (datafeed, updateFrequency) { this._datafeed = datafeed; this._subscribers = {}; this._re = 0; var that = this; var update = function () { if (that._re > 0) { return; } for (var listenerGUID in that._subscribers) { var subscriptionRecord = that._subscribers[listenerGUID]; var resolution = subscriptionRecord.resolution; var datesRangeRight = parseInt(new Date().valueOf() / 1000); // BEWARE: please note we really need 2 bars, not the only last one // see the explanation below. `10` is the `large enough` value to work around holidays var datesRangeLeft = datesRangeRight - that.periodLengthSeconds(resolution, 10); that._re++; (function (_subscriptionRecord) { // eslint-disable-line that._datafeed.getBars( _subscriptionRecord.symbolInfo, resolution, datesRangeLeft, datesRangeRight, function (bars) { that._re--; // means the subscription was cancelled while waiting for data if (!that._subscribers.hasOwnProperty(listenerGUID)) { return; } if (bars.length === 0) { return; } var lastBar = bars[bars.length - 1]; if ( !isNaN(_subscriptionRecord.lastBarTime) && lastBar.time if (bars.length subscribers[i](previousBar); } } _subscriptionRecord.lastBarTime = lastBar.time; for (var i = 0; i that._re--; } ); })(subscriptionRecord); } }; if (typeof updateFrequency != "undefined" && updateFrequency > 0) { setInterval(update, updateFrequency); } }; Datafeed.DataPulseUpdater.prototype.periodLengthSeconds = function ( resolution, re ) { var daysCount = 0; if (resolution === "D") { daysCount = re; } else if (resolution === "M") { daysCount = 31 * re; } else if (resolution === "W") { daysCount = 7 * re; } else { daysCount = (re * resolution) / (24 * 60); } return daysCount * 24 * 60 * 60; }; Datafeed.DataPulseUpdater.prototype.subscribeDataListener = function ( symbolInfo, resolution, newDataCallback, listenerGUID ) { this._datafeed._logMessage("Subscribing " + listenerGUID); if (!this._subscribers.hasOwnProperty(listenerGUID)) { this._subscribers[listenerGUID] = { symbolInfo: symbolInfo, resolution: resolution, lastBarTime: NaN, listeners: [], }; } this._subscribers[listenerGUID].listeners.push(newDataCallback); }; Datafeed.DataPulseUpdater.prototype.unsubscribeDataListener = function ( listenerGUID ) { this._datafeed._logMessage("Unsubscribing " + listenerGUID); delete this._subscribers[listenerGUID]; }; Datafeed.Container = function (updateFrequency) { this._configuration = { supports_search: false, supports_group_request: false, supported_resolutions: [ "1", "3", "5", "15", "30", "60", "120", "240", "360", "720", "1D", "3D", "1W", "1M", ], supports_marks: true, supports_timescale_marks: true, exchanges: ["gh"], }; this._barsPulseUpdater = new Datafeed.DataPulseUpdater( this, updateFrequency || 10 * 1000 ); // this._ = new Datafeed.(this); this._enableLogging = true; this._callbacks = {}; this._initializationFinished = true; this._fireEvent("initialized"); this._fireEvent("configuration_ready"); }; Datafeed.Container.prototype._fireEvent = function (event, argument) { if (this._callbacks.hasOwnProperty(event)) { var callbacksChain = this._callbacks[event]; for (var i = 0; i if (this._enableLogging) { var now = new Date(); // console.log("CHART LOGS: "+now.toLocaleTimeString() + '.' + now.getMilliseconds() + '> ' + message); } }; Datafeed.Container.prototype.on = function (event, callback) { if (!this._callbacks.hasOwnProperty(event)) { this._callbacks[event] = []; } this._callbacks[event].push(callback); return this; }; Datafeed.Container.prototype.onReady = function (callback) { let that = this; if (that._configuration) { setTimeout(function () { callback(that._configuration); }, 0); } else { this.on("configuration_ready", function () { callback(that._configuration); }); } }; Datafeed.Container.prototype.resolveSymbol = function ( symbolName, onSymbolResolvedCallback, onResolveErrorCallback ) { this._logMessage("GOWNO :: resolve symbol " + symbolName); Promise.resolve().then(() => { // console.log(this_vue.priceScale,'12345s313123122adaslast') // this._logMessage("GOWNO :: onResultReady inject "+'AAPL'); // console.log(this_vue.priceScale,'123stf') onSymbolResolvedCallback({ name: this_vue.symbol, timezone: "Asia/Shanghai", pricescale: this_vue.priceScale, minmov: 1, //minmov(最小波动), pricescale(价格精度), minmove2, fractional(分数) minmov2: 0, //这是一个神奇的数字来格式化复杂情况下的价格。 ticker: this_vue.symbol, description: "", type: "bitcoin", volume_precision: 8, // "exchange-traded": "sdt", // "exchange-listed": "sdt", //现在,这两个字段都为某个交易所的略称。将被显示在图表的图例中,以表示此商品。目前此字段不用于其他目的。 has_intraday: true, has_weekly_and_monthly: true, has_no_volume: false, //布尔表示商品是否拥有成交量数据。 session: "24x7", supported_resolutions: [ "1", "3", "5", "15", "30", "60", "120", "240", "360", "720", "1D", "3D", "1W", "1M", ], }); }); }; //初始化数据 Datafeed.Container.prototype.getBars = function ( symbolInfo, resolution, rangeStartDate, rangeEndDate, onHistoryCallback, onErrorCallback ) { if ( resolution.indexOf("D") == -1 && resolution.indexOf("W") == -1 && resolution.indexOf("M") == -1 ) { resolution = resolution + "min"; } else if ( resolution.indexOf("W") != -1 || resolution.indexOf("M") != -1 ) { resolution = resolution; } $.ajax({ //XXXXXXXXXXXXX 是后台给你的接口 url: "XXXXXXXXXXXXX?" + "from=" + rangeStartDate + "&to=" + rangeEndDate + "&symbol=" + symbolInfo.name + "&period=" + resolution, type: "get", success: function (res) { if (res.code == 1 && res.data && res.data.length > 0) { res.data.forEach((item, i) => { item.open = Number(item.open); item.close = Number(item.close); item.high = Number(item.high); item.low = Number(item.low); }); onHistoryCallback(res.data, { noData: false }); onHistoryCallback([], { noData: true }); } if (!res.data || res.code == -1) { onHistoryCallback([], { noData: true }); } if (res.data && res.data.length == 0) { onHistoryCallback([], { noData: true }); } }, }); }; //实时数据 Datafeed.Container.prototype.subscribeBars = function ( symbolInfo, resolution, onRealtimeCallback, listenerGUID, onResetCacheNeededCallback ) { this_vue.connect(onRealtimeCallback); //this._barsPulseUpdater.subscribeDataListener(symbolInfo, resolution, onRealtimeCallback, listenerGUID, onResetCacheNeededCallback); }; Datafeed.Container.prototype.unsubscribeBars = function (listenerGUID) { this._barsPulseUpdater.unsubscribeDataListener(listenerGUID); }; return new Datafeed.Container(); },

第六步:
开始增加线条颜色
下面颜色是上面获取的,不需要写死就行

overrides() 完整代码

overrides() { let style = { up: "#fa5252", down: "#12b886", bg: this.bg_, grid: this.grid, cross: this.cross, border: "#4e5b85", text: "#61688A", areatop: "rgba(122, 152, 247, .1)", areadown: "rgba(122, 152, 247, .02)", }; return { volumePaneSize: "medium", //large, medium, small, tiny "paneProperties.topMargin": "20", "scalesProperties.lineColor": style.text, "scalesProperties.textColor": style.text, "paneProperties.background": style.bg, //改变背景色的重要代码 "paneProperties.vertGridProperties.color": style.grid, "paneProperties.horzGridProperties.color": style.grid, "paneProperties.crossHairProperties.color": style.cross, "paneProperties.legendProperties.showLegend": true, "paneProperties.legendProperties.showStudyArguments": true, "paneProperties.legendProperties.showStudyTitles": true, "paneProperties.legendProperties.showStudyValues": true, "paneProperties.legendProperties.showSeriesTitle": true, "paneProperties.legendProperties.showSeriesOHLC": true, "mainSeriesProperties.candleStyle.upColor": style.up, "mainSeriesProperties.candleStyle.downColor": style.down, "mainSeriesProperties.candleStyle.drawWick": true, "mainSeriesProperties.candleStyle.drawBorder": true, "mainSeriesProperties.candleStyle.borderColor": style.border, "mainSeriesProperties.candleStyle.borderUpColor": style.up, "mainSeriesProperties.candleStyle.borderDownColor": style.down, "mainSeriesProperties.candleStyle.wickUpColor": style.up, "mainSeriesProperties.candleStyle.wickDownColor": style.down, "mainSeriesProperties.candleStyle.barColorsOnPrevClose": false, "mainSeriesProperties.hollowCandleStyle.upColor": style.up, "mainSeriesProperties.hollowCandleStyle.downColor": style.down, "mainSeriesProperties.hollowCandleStyle.drawWick": true, "mainSeriesProperties.hollowCandleStyle.drawBorder": true, "mainSeriesProperties.hollowCandleStyle.borderColor": style.border, "mainSeriesProperties.hollowCandleStyle.borderUpColor": style.up, "mainSeriesProperties.hollowCandleStyle.borderDownColor": style.down, "mainSeriesProperties.hollowCandleStyle.wickColor": style.line, "mainSeriesProperties.haStyle.upColor": style.up, "mainSeriesProperties.haStyle.downColor": style.down, "mainSeriesProperties.haStyle.drawWick": true, "mainSeriesProperties.haStyle.drawBorder": true, "mainSeriesProperties.haStyle.borderColor": style.border, "mainSeriesProperties.haStyle.borderUpColor": style.up, "mainSeriesProperties.haStyle.borderDownColor": style.down, "mainSeriesProperties.haStyle.wickColor": style.border, "mainSeriesProperties.haStyle.barColorsOnPrevClose": false, "mainSeriesProperties.barStyle.upColor": style.up, "mainSeriesProperties.barStyle.downColor": style.down, "mainSeriesProperties.barStyle.barColorsOnPrevClose": false, "mainSeriesProperties.barStyle.dontDrawOpen": false, "mainSeriesProperties.lineStyle.color": style.border, "mainSeriesProperties.lineStyle.linewidth": 2, "mainSeriesProperties.lineStyle.priceSource": "close", "mainSeriesProperties.areaStyle.color1": style.areatop, "mainSeriesProperties.areaStyle.color2": style.areadown, "mainSeriesProperties.areaStyle.linecolor": style.border, "mainSeriesProperties.areaStyle.linewidth": 2, "mainSeriesProperties.areaStyle.priceSource": "close", }; },

第七步:
构建 updateData() 函数 ,updateWidget() 函数,removeWidget() 函数 (也都是放在methods里面,内容不是特别多就放一块写了)
完整代码

// 更新图表 updateData(data) { if (data) { this.$emit("real-time", data); } }, updateWidget(item) { this.symbolInfo = { name: item, ticker: item, description: "", session: "24x7", supported_resolutions: [ "1", "3", "5", "15", "30", "60", "120", "240", "360", "720", "1D", "3D", "1W", "1M" ], has_intraday: true, has_daily: true, has_weekly_and_monthly: true, timezone: "UTC" }; this.removeWidget(); this.createWidget(); }, //销毁图表 removeWidget() { if (this.widget) { this.widget.remove(); this.widget = null; } },

第八步:
createWidget() 记得要在mounted里面引用
记得在destroyed里面销毁一下

第九步:
tv-top的建立,显示高低价和涨幅榜
(numFilter为我的截取小数点的管道符,根据自己的进行调节)

然后数据展示内容是后台返回实时更新的
全部代码如下:

{{lastPrice | numFilter}} lastPrice*usprice}}CNY --> 涨幅:{{downUp}}% {{coin.high || 0 | numFilter }} {{coin.low || 0 | numFilter }} export default { data() { return { leftName: "", rightName: "", legalId: "", curencyId: "", coin: {}, lastPrice: "", downUp: "", showMarket: false, usprice: "", socket: "", symbol: "", }; }, computed: { }, destroyed() { if (this.socket) { this.socket.disconnect(); } }, created() { // this.leftName = window.localStorage.getItem("legal_name") || ""; // this.rightName = window.localStorage.getItem("currency_name") || ""; this.currencyId = window.localStorage.getItem("legal_id"); this.legalId = window.localStorage.getItem("currency_id"); //console.log(this.currencyId+"||||||||"+this.legalId); this.init(this.legalId, this.currencyId); this.downUp = window.localStorage.getItem("downUp"); this.getKlinke(); this.symbol = "BTC/USDT"; this.leftName = this.symbol.split("/")[1] || ""; this.rightName = this.symbol.split("/")[0] || ""; }, mounted() { this.leftName = this.symbol.split("/")[1] || ""; this.rightName = this.symbol.split("/")[0] || ""; var that = this; eventBus.$on("toNew", function (msg) { console.log("laaaaaaaaa", msg); var thattoken = that.rightName + "/" + that.leftName; if (msg.istoken == thattoken) { that.lastPrice = (msg.newprice - 0).toFixed(4); that.downUp = (msg.newup - 0).toFixed(2); // that.coin.total = msg.hour24; } }); this.socket = this.$socketio.connect("http://8.210.87.26:2120", { transports: ["websocket", "xhr-polling", "jsonp-polling"] }); var socket = this.socket; socket.on("daymarket", msg => { if (msg.type == "transaction") { if (that.rightName + "/" + that.leftName == msg.token) { if (msg["24h"]) { that.coin = JSON.parse(msg["24h"]); } } } }); }, sockets: { daymarket: function(msg) { let that = this; if (msg.type == "daymarket") { if (that.rightName + "/" + that.leftName == msg.symbol) { if (msg["24h"]) { that.coin = JSON.parse(msg["24h"]); } that.lastPrice = msg.now_price; that.downUp = msg.change; that.coin.high = msg.high; that.coin.low = msg.low; that.coin.total = Number(msg.volume).toFixed(4); } } } }, methods: { init(legalId, currencyId) { this.$http .post("http://www.pkex.in/api/transaction/deal", { legal_id: 23, currency_id: 32, }) .then((res) => { //console.log(res); if (res.type == "ok") { this.lastPrice = res.message.last_price; } }); }, getKlinke() { let that = this; this.socket = this.$socketio.connect("http://8.210.87.26:2120", { transports: ["websocket", "xhr-polling", "jsonp-

本文来源:https://www.kucaijing.com/articles/48025.html

标签组:[vue

相关APP下载

扩展阅读文章

热门话题

科技推荐文章

科技热门文章