| | |
| | | <template> |
| | | <div id="cryptos"> |
| | | <div > |
| | | <div class="kline-charts"> |
| | | <ul class="flex px-32 pb-20 box-border justify-between text-grey fontStyle font-26" |
| | | style="border-bottom:1px solid rgba(68,75,88,0.2);"> |
| | |
| | | </template> |
| | | </template> |
| | | </ul> |
| | | <div class="relative"> |
| | | <div id="kline" class="h-828"></div> |
| | | <div |
| | | class="flex justify-center items-center text-center text-grey absolute left-0 top-0 w-full h-full z-10 mainBackground" |
| | | v-if="chartLoading"> |
| | | <van-loading type="spinner"></van-loading> |
| | | <div class="chart-with-tools flex"> |
| | | <div class="relative flex-1 min-w-0"> |
| | | <div id="kline" class="h-828"></div> |
| | | <div |
| | | class="flex justify-center items-center text-center text-grey absolute left-0 top-0 w-full h-full z-10 mainBackground" |
| | | v-if="chartLoading"> |
| | | <van-loading type="spinner"></van-loading> |
| | | </div> |
| | | </div> |
| | | <!-- 铅笔图标:控制绘图工具栏显示/隐藏 --> |
| | | <div class="draw-tools-wrap flex"> |
| | | <div class="pencil-trigger draw-tool-item" :class="[isNight ? 'draw-toolbar-night' : 'draw-toolbar-light', { active: showDrawToolbar }]" |
| | | :title="showDrawToolbar ? $t('隐藏绘图工具') : $t('显示绘图工具')" @click="showDrawToolbar = !showDrawToolbar"> |
| | | <svg viewBox="0 0 24 24" width="20" height="20"><path fill="currentColor" d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg> |
| | | </div> |
| | | <!-- 绘图工具栏 --> |
| | | <div v-show="showDrawToolbar" class="draw-toolbar" :class="isNight ? 'draw-toolbar-night' : 'draw-toolbar-light'"> |
| | | <div class="draw-tool-item" :class="{ active: activeDrawTool === 'fibonacciLine' }" title="斐波那契" @click="onDrawToolClick('fibonacciLine')"> |
| | | <svg viewBox="0 0 24 24" width="20" height="20"><path fill="currentColor" d="M4 20L20 4l-2-2L2 18v4h2z"/><circle cx="6" cy="18" r="1.5"/><circle cx="18" cy="6" r="1.5"/></svg> |
| | | </div> |
| | | <div class="draw-tool-item" :class="{ active: activeDrawTool === 'segment' }" title="线段" @click="onDrawToolClick('segment')"> |
| | | <svg viewBox="0 0 24 24" width="20" height="20"><line x1="4" y1="20" x2="20" y2="4" stroke="currentColor" stroke-width="2"/><circle cx="4" cy="20" r="2"/><circle cx="20" cy="4" r="2"/></svg> |
| | | </div> |
| | | <div class="draw-tool-item" :class="{ active: activeDrawTool === 'horizontalStraightLine' }" title="水平线" @click="onDrawToolClick('horizontalStraightLine')"> |
| | | <svg viewBox="0 0 24 24" width="20" height="20"><line x1="2" y1="12" x2="22" y2="12" stroke="currentColor" stroke-width="2"/><circle cx="12" cy="12" r="2"/></svg> |
| | | </div> |
| | | <div class="draw-tool-item" :class="{ active: activeDrawTool === 'horizontalSegment' }" title="水平线段" @click="onDrawToolClick('horizontalSegment')"> |
| | | <svg viewBox="0 0 24 24" width="20" height="20"><line x1="6" y1="12" x2="18" y2="12" stroke="currentColor" stroke-width="2"/><circle cx="6" cy="12" r="2"/><circle cx="18" cy="12" r="2"/></svg> |
| | | </div> |
| | | <div class="draw-tool-item" :class="{ active: activeDrawTool === 'verticalStraightLine' }" title="垂直线" @click="onDrawToolClick('verticalStraightLine')"> |
| | | <svg viewBox="0 0 24 24" width="20" height="20"><line x1="12" y1="2" x2="12" y2="22" stroke="currentColor" stroke-width="2"/><circle cx="12" cy="12" r="2"/></svg> |
| | | </div> |
| | | <div class="draw-tool-item" :class="{ active: activeDrawTool === 'verticalSegment' }" title="垂直线段" @click="onDrawToolClick('verticalSegment')"> |
| | | <svg viewBox="0 0 24 24" width="20" height="20"><line x1="12" y1="6" x2="12" y2="18" stroke="currentColor" stroke-width="2"/><circle cx="12" cy="6" r="2"/><circle cx="12" cy="18" r="2"/></svg> |
| | | </div> |
| | | <div class="draw-tool-item" :class="{ active: activeDrawTool === 'rect' }" title="矩形" @click="onDrawToolClick('rect')"> |
| | | <svg viewBox="0 0 24 24" width="20" height="20"><rect x="5" y="7" width="14" height="10" fill="none" stroke="currentColor" stroke-width="2"/><circle cx="5" cy="7" r="1.5"/><circle cx="19" cy="17" r="1.5"/></svg> |
| | | </div> |
| | | <div class="draw-tool-item" :class="{ active: activeDrawTool === 'triangle' }" title="三角形" @click="onDrawToolClick('triangle')"> |
| | | <svg viewBox="0 0 24 24" width="20" height="20"><path d="M12 5L4 19h16L12 5z" fill="none" stroke="currentColor" stroke-width="2"/><circle cx="12" cy="5" r="1.5"/><circle cx="4" cy="19" r="1.5"/><circle cx="20" cy="19" r="1.5"/></svg> |
| | | </div> |
| | | <div class="draw-tool-item" :class="{ active: activeDrawTool === 'parallelogram' }" title="平行四边形" @click="onDrawToolClick('parallelogram')"> |
| | | <svg viewBox="0 0 24 24" width="20" height="20"><path d="M6 6h12l-4 12H2L6 6z" fill="none" stroke="currentColor" stroke-width="2"/><circle cx="6" cy="6" r="1.5"/><circle cx="18" cy="6" r="1.5"/><circle cx="14" cy="18" r="1.5"/></svg> |
| | | </div> |
| | | <div class="draw-tool-item" title="清除全部" @click="onDrawToolClick('remove')"> |
| | | <svg viewBox="0 0 24 24" width="20" height="20"><path fill="currentColor" d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <ul class="flex px-32 py-20 box-border justify-between text-grey font-26" v-if="showBottom" |
| | |
| | | import config from './config' |
| | | import { clearAllTimers } from '@/utils/utis.js' |
| | | import { Loading } from 'vant'; |
| | | import { customShapeTemplates } from './drawTools' |
| | | export default { |
| | | name: 'KlineCharts', |
| | | data() { |
| | |
| | | chartLoading: true, //加载动画 |
| | | paneId: '', |
| | | chartData: [], // 图表数据 |
| | | timer: null |
| | | timer: null, |
| | | activeDrawTool: '', // 当前选中的绘图工具 |
| | | showDrawToolbar: true // 绘图工具栏是否显示,由铅笔图标切换 |
| | | } |
| | | }, |
| | | computed: { |
| | | timeList() { |
| | | return [ |
| | | { id: '1min', time: '1min', text: this.$t('分时'), ts: 1 * 60 * 1000 }, |
| | | { id: '1mins', time: '1min', text: '1' + this.$t('分'), ts: 1 * 60 * 1000 }, |
| | | // { id: '1mins', time: '1min', text: '1' + this.$t('分'), ts: 1 * 60 * 1000 }, |
| | | { id: '5min', time: '5min', text: '5' + this.$t('分'), ts: 5 * 60 * 1000 }, |
| | | { id: '15min', time: '15min', text: '15' + this.$t('分'), ts: 15 * 60 * 1000 }, |
| | | { id: '30min', time: '30min', text: '30' + this.$t('分'), ts: 30 * 60 * 1000 }, |
| | | { id: '60min', time: '60min', text: '1' + this.$t('小时'), ts: 60 * 60 * 1000 }, |
| | | { id: '4hour', time: '4hour', text: '4' + this.$t('小时'), ts: 4 * 60 * 60 * 1000 }, |
| | | // { id: '4hour', time: '4hour', text: '4' + this.$t('小时'), ts: 4 * 60 * 60 * 1000 }, |
| | | { id: '1day', time: '1day', text: '1' + this.$t('天'), ts: 24 * 60 * 60 * 1000 }, |
| | | { id: '1week', time: '1week', text: '1' + this.$t('周'), ts: 7 * 24 * 60 * 60 * 1000 }, |
| | | { id: '1mon', time: '1mon', text: '1' + this.$t('月'), ts: 30 * 24 * 60 * 60 * 1000 } |
| | |
| | | this.clearTimer() |
| | | this.fetchData() |
| | | }, |
| | | updateKey() { // 更新charts |
| | | updateKey() { // 更新charts:只更新 this.updateData 的 close,其余沿用最后一根 |
| | | const dataList = chart.getDataList() |
| | | if (dataList.length > 0) { |
| | | const lastData = dataList[dataList.length - 1] |
| | | const nowData = this.updateData |
| | | const timeValue = this.timeValue |
| | | const newData = { |
| | | close: nowData.close / 1, |
| | | current_time: lastData.current_time, |
| | | high: lastData.high > nowData.close / 1 ? lastData.high : (nowData.close / 1), |
| | | // high: lastData.high, |
| | | line: timeValue.id, |
| | | low: lastData.low < nowData.close / 1 ? lastData.low : (nowData.close / 1), |
| | | // low: lastData.low, |
| | | open: lastData.open, |
| | | symbol: lastData.symbol, |
| | | ts: lastData.ts, // |
| | | timestamp: (nowData.ts - lastData.ts) < timeValue.ts ? lastData.ts : (lastData.ts + timeValue.ts), |
| | | volume: lastData.volume / 1 |
| | | ...lastData, |
| | | close: nowData.close != null && nowData.close !== '' ? (nowData.close / 1) : lastData.close |
| | | } |
| | | this.$nextTick(() => { |
| | | this.setChartType() |
| | |
| | | }, |
| | | methods: { |
| | | initData() { |
| | | this.timeValue = this.timeList[0] |
| | | this.timeValue = this.timeList.find(t => t.id === '15min') || this.timeList[0] |
| | | chart = init('kline', config); |
| | | chart.setOffsetRightSpace(25) |
| | | chart.setDataSpace(10) |
| | | chart.setPriceVolumePrecision(4, 2) |
| | | chart.createTechnicalIndicator('MA', false, { id: 'candle_pane' }); |
| | | this.paneId = chart.createTechnicalIndicator('VOL'); |
| | | // 注册自定义绘图图形:矩形、三角形、平行四边形 |
| | | chart.addShapeTemplate(customShapeTemplates) |
| | | this.fetchData() |
| | | }, |
| | | fetchData(time) { // 获取数据 |
| | |
| | | chart.applyNewData(data); |
| | | this.$emit('updataLine', false) |
| | | }) |
| | | }, 30000); |
| | | }, 10000); |
| | | }, |
| | | setChartType() { |
| | | let type = 'area' |
| | |
| | | this.timer = null |
| | | clearAllTimers() |
| | | }, |
| | | onDrawToolClick(name) { |
| | | if (!chart) return |
| | | if (name === 'remove') { |
| | | chart.removeShape() |
| | | this.activeDrawTool = '' |
| | | return |
| | | } |
| | | this.activeDrawTool = name |
| | | chart.createShape(name, 'candle_pane') |
| | | }, |
| | | }, |
| | | deactivated() { |
| | | this.clearTimer() |
| | |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | @import "@/assets/init.scss"; |
| | | // #kline { |
| | | // // min-height: 828px; |
| | | // height: 1200px; |
| | | // }</style> |
| | | |
| | | .chart-with-tools { |
| | | position: relative; |
| | | } |
| | | |
| | | .draw-tools-wrap { |
| | | flex-shrink: 0; |
| | | display: flex; |
| | | border-left: 1px solid rgba(68, 75, 88, 0.2); |
| | | } |
| | | |
| | | .draw-tools-wrap .pencil-trigger { |
| | | width: 44px; |
| | | min-width: 44px; |
| | | flex-shrink: 0; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 8px 0; |
| | | cursor: pointer; |
| | | transition: background 0.2s; |
| | | border-radius: 0; |
| | | } |
| | | .pencil-trigger.draw-toolbar-light { |
| | | background: rgba(255, 255, 255, 0.98); |
| | | color: #333; |
| | | } |
| | | .pencil-trigger.draw-toolbar-light:hover, |
| | | .pencil-trigger.draw-toolbar-light.active { |
| | | background: rgba(0, 0, 0, 0.06); |
| | | color: #2196f3; |
| | | } |
| | | .pencil-trigger.draw-toolbar-night { |
| | | background: rgba(17, 26, 46, 0.98); |
| | | color: #d9d9d9; |
| | | } |
| | | .pencil-trigger.draw-toolbar-night:hover, |
| | | .pencil-trigger.draw-toolbar-night.active { |
| | | background: rgba(255, 255, 255, 0.08); |
| | | color: #42a5f5; |
| | | } |
| | | |
| | | .draw-toolbar { |
| | | width: 44px; |
| | | flex-shrink: 0; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | padding: 8px 0; |
| | | gap: 4px; |
| | | } |
| | | |
| | | .draw-toolbar-light { |
| | | background: rgba(255, 255, 255, 0.98); |
| | | color: #333; |
| | | } |
| | | |
| | | .draw-toolbar-night { |
| | | background: rgba(17, 26, 46, 0.98); |
| | | color: #d9d9d9; |
| | | border-left-color: rgba(68, 75, 88, 0.4); |
| | | } |
| | | |
| | | .draw-tool-item { |
| | | width: 36px; |
| | | height: 36px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | border-radius: 6px; |
| | | cursor: pointer; |
| | | transition: background 0.2s; |
| | | } |
| | | |
| | | .draw-toolbar-light .draw-tool-item:hover { |
| | | background: rgba(0, 0, 0, 0.06); |
| | | } |
| | | |
| | | .draw-toolbar-light .draw-tool-item.active { |
| | | background: rgba(33, 150, 243, 0.15); |
| | | color: #2196f3; |
| | | } |
| | | |
| | | .draw-toolbar-night .draw-tool-item:hover { |
| | | background: rgba(255, 255, 255, 0.08); |
| | | } |
| | | |
| | | .draw-toolbar-night .draw-tool-item.active { |
| | | background: rgba(33, 150, 243, 0.25); |
| | | color: #42a5f5; |
| | | } |
| | | |
| | | .draw-tool-item svg { |
| | | display: block; |
| | | } |
| | | </style> |