trading-vue3-js
Version:
Customizable charting lib for traders. Based on https://github.com/C451/trading-vue-js by C451.
302 lines (239 loc) • 7.97 kB
JavaScript
import * as Hammer from 'hammerjs'
import Utils from '../../stuff/utils.js'
import math from '../../stuff/math.js'
var PANHEIGHT
export default class Sidebar {
constructor(canvas, comp, side = 'right') {
PANHEIGHT = comp.config.PANHEIGHT
this.canvas = canvas
this.ctx = canvas.getContext('2d')
this.comp = comp
this.$p = comp.$props
this.data = this.$p.sub
this.range = this.$p.range
this.id = this.$p.grid_id
this.layout = this.$p.layout.grids[this.id]
this.side = side
this.listeners()
}
listeners() {
let mc = this.mc = new Hammer.Manager(this.canvas)
mc.add(new Hammer.Pan({
direction: Hammer.DIRECTION_VERTICAL,
threshold: 0
}))
mc.add( new Hammer.Tap({
event: 'doubletap',
taps: 2,
posThreshold: 50
}))
mc.on('panstart', event => {
if (this.$p.y_transform) {
this.zoom = this.$p.y_transform.zoom
} else {
this.zoom = 1.0
}
this.y_range = [
this.layout.$_hi,
this.layout.$_lo
]
this.drug = {
y: event.center.y,
z: this.zoom,
mid: math.log_mid(this.y_range, this.layout.height),
A: this.layout.A,
B: this.layout.B
}
})
mc.on('panmove', event => {
if (this.drug) {
this.zoom = this.calc_zoom(event)
this.comp.$emit('sidebar-transform', {
grid_id: this.id,
zoom: this.zoom,
auto: false,
range: this.calc_range(),
drugging: true
})
this.update()
}
})
mc.on('panend', () => {
this.drug = null
this.comp.$emit('sidebar-transform', {
grid_id: this.id,
drugging: false
})
})
mc.on('doubletap', () => {
this.comp.$emit('sidebar-transform', {
grid_id: this.id,
zoom: 1.0,
auto: true
})
this.zoom = 1.0
this.update()
})
// TODO: Do later for mobile version
}
update() {
// Update reference to the grid
this.layout = this.$p.layout.grids[this.id]
var points = this.layout.ys
var x, y, w, h, side = this.side
var sb = this.layout.sb
//this.ctx.fillStyle = this.$p.colors.back
this.ctx.font = this.$p.font
switch(side) {
case 'left':
x = 0
y = 0
w = Math.floor(sb)
h = this.layout.height
//this.ctx.fillRect(x, y, w, h)
this.ctx.clearRect(x, y, w, h)
this.ctx.strokeStyle = this.$p.colors.scale
this.ctx.beginPath()
this.ctx.moveTo(x + 0.5, 0)
this.ctx.lineTo(x + 0.5, h)
this.ctx.stroke()
break
case 'right':
x = 0
y = 0
w = Math.floor(sb)
h = this.layout.height
//this.ctx.fillRect(x, y, w, h)
this.ctx.clearRect(x, y, w, h)
this.ctx.strokeStyle = this.$p.colors.scale
this.ctx.beginPath()
this.ctx.moveTo(x + 0.5, 0)
this.ctx.lineTo(x + 0.5, h)
this.ctx.stroke()
break
}
this.ctx.fillStyle = this.$p.colors.text
this.ctx.beginPath()
for (var p of points) {
if (p[0] > this.layout.height) continue
var x1 = side === 'left' ? w - 0.5 : x - 0.5
var x2 = side === 'left' ? x1 - 4.5 : x1 + 4.5
this.ctx.moveTo(x1, p[0] - 0.5)
this.ctx.lineTo(x2, p[0] - 0.5)
var offst = side === 'left' ? - 10 : 10
this.ctx.textAlign = side === 'left' ? 'end' : 'start'
let d = this.layout.prec
this.ctx.fillText(p[1].toFixed(d), x1 + offst, p[0] + 4)
}
this.ctx.stroke()
if (this.$p.grid_id) this.upper_border()
this.apply_shaders()
if (this.$p.cursor.y && this.$p.cursor.y$) this.panel()
}
apply_shaders() {
let layout = this.$p.layout.grids[this.id]
let props = {
layout: layout,
cursor: this.$p.cursor
}
for (var s of this.$p.shaders) {
this.ctx.save()
s.draw(this.ctx, props)
this.ctx.restore()
}
}
upper_border() {
this.ctx.strokeStyle = this.$p.colors.scale
this.ctx.beginPath()
this.ctx.moveTo(0, 0.5)
this.ctx.lineTo(this.layout.width, 0.5)
this.ctx.stroke()
}
// A gray bar behind the current price
panel() {
if (this.$p.cursor.grid_id !== this.layout.id) {
return
}
let lbl = this.$p.cursor.y$.toFixed(this.layout.prec)
this.ctx.fillStyle = this.$p.colors.panel
let panwidth = this.layout.sb + 1
let x = - 0.5
let y = this.$p.cursor.y - PANHEIGHT * 0.5 - 0.5
let a = 7
this.ctx.fillRect(x - 0.5, y, panwidth, PANHEIGHT)
this.ctx.fillStyle = this.$p.colors.textHL
this.ctx.textAlign = 'left'
this.ctx.fillText(lbl, a, y + 15)
}
calc_zoom(event) {
let d = this.drug.y - event.center.y
let speed = d > 0 ? 3 : 1
let k = 1 + speed * d / this.layout.height
return Utils.clamp(this.drug.z * k, 0.005, 100)
}
// Not the best place to calculate y-range but
// this is the simplest solution I found up to
// date
calc_range(diff1 = 1, diff2 = 1) {
let z = this.zoom / this.drug.z
let zk = (1 / z - 1) / 2
let range = this.y_range.slice()
let delta = range[0] - range[1]
if (!this.layout.grid.logScale) {
range[0] = range[0] + delta * zk * diff1
range[1] = range[1] - delta * zk * diff2
} else {
let px_mid = this.layout.height / 2
let new_hi = px_mid - px_mid * (1/z)
let new_lo = px_mid + px_mid * (1/z)
// Use old mapping to get a new range
let f = y => math.exp((y - this.drug.B) / this.drug.A)
let copy = range.slice()
range[0] = f(new_hi)
range[1] = f(new_lo)
}
return range
}
rezoom_range(delta, diff1, diff2) {
if (!this.$p.y_transform || this.$p.y_transform.auto) return
this.zoom = 1.0
// TODO: further work (improve scaling ratio)
if (delta < 0) delta /= 3.75 // Btw, idk why 3.75, but it works
delta *= 0.25
this.y_range = [
this.layout.$_hi,
this.layout.$_lo
]
this.drug = {
y: 0,
z: this.zoom,
mid: math.log_mid(this.y_range, this.layout.height),
A: this.layout.A,
B: this.layout.B
}
this.zoom = this.calc_zoom({
center: {
y: delta * this.layout.height
}
})
this.comp.$emit('sidebar-transform', {
grid_id: this.id,
zoom: this.zoom,
auto: false,
range: this.calc_range(diff1, diff2),
drugging: true
})
this.drug = null
this.comp.$emit('sidebar-transform', {
grid_id: this.id,
drugging: false
})
}
destroy() {
if (this.mc) this.mc.destroy()
}
mousemove() { }
mouseout() { }
mouseup() { }
mousedown() { }
}