| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- import color from '@jiaminghi/color'
- import bezierCurve from '@jiaminghi/bezier-curve'
- import { deepClone } from '../plugin/util'
- import allGraph from '../config/graphs'
- import Graph from './graph.class'
- /**
- * @description Class of CRender
- * @param {Object} canvas Canvas DOM
- * @return {CRender} Instance of CRender
- */
- export default class CRender {
- constructor (canvas) {
- if (!canvas) {
- console.error('CRender Missing parameters!')
- return
- }
- const ctx = canvas.getContext('2d')
- const { clientWidth, clientHeight } = canvas
- const area = [clientWidth, clientHeight]
- canvas.setAttribute('width', clientWidth)
- canvas.setAttribute('height', clientHeight)
- /**
- * @description Context of the canvas
- * @type {Object}
- * @example ctx = canvas.getContext('2d')
- */
- this.ctx = ctx
- /**
- * @description Width and height of the canvas
- * @type {Array}
- * @example area = [300,100]
- */
- this.area = area
- /**
- * @description Whether render is in animation rendering
- * @type {Boolean}
- * @example animationStatus = true|false
- */
- this.animationStatus = false
- /**
- * @description Added graph
- * @type {[Graph]}
- * @example graphs = [Graph, Graph, ...]
- */
- this.graphs = []
- /**
- * @description Color plugin
- * @type {Object}
- * @link https://github.com/jiaming743/color
- */
- this.color = color
- /**
- * @description Bezier Curve plugin
- * @type {Object}
- * @link https://github.com/jiaming743/BezierCurve
- */
- this.bezierCurve = bezierCurve
- // bind event handler
- canvas.addEventListener('mousedown', mouseDown.bind(this))
- canvas.addEventListener('mousemove', mouseMove.bind(this))
- canvas.addEventListener('mouseup', mouseUp.bind(this))
- }
- }
- /**
- * @description Clear canvas drawing area
- * @return {Undefined} Void
- */
- CRender.prototype.clearArea = function () {
- const { area } = this
- this.ctx.clearRect(0, 0, ...area)
- }
- /**
- * @description Add graph to render
- * @param {Object} config Graph configuration
- * @return {Graph} Graph instance
- */
- CRender.prototype.add = function (config = {}) {
- const { name } = config
- if (!name) {
- console.error('add Missing parameters!')
- return
- }
- const graphConfig = allGraph.get(name)
- if (!graphConfig) {
- console.warn('No corresponding graph configuration found!')
- return
- }
- const graph = new Graph(graphConfig, config)
- if (!graph.validator(graph)) return
- graph.render = this
- this.graphs.push(graph)
- this.sortGraphsByIndex()
- this.drawAllGraph()
- return graph
- }
- /**
- * @description Sort the graph by index
- * @return {Undefined} Void
- */
- CRender.prototype.sortGraphsByIndex = function () {
- const { graphs } = this
- graphs.sort((a, b) => {
- if (a.index > b.index) return 1
- if (a.index === b.index) return 0
- if (a.index < b.index) return -1
- })
- }
- /**
- * @description Delete graph in render
- * @param {Graph} graph The graph to be deleted
- * @return {Undefined} Void
- */
- CRender.prototype.delGraph = function (graph) {
- if (typeof graph.delProcessor !== 'function') return
- graph.delProcessor(this)
- this.graphs = this.graphs.filter(graph => graph)
- this.drawAllGraph()
- }
- /**
- * @description Delete all graph in render
- * @return {Undefined} Void
- */
- CRender.prototype.delAllGraph = function () {
- this.graphs.forEach(graph => graph.delProcessor(this))
- this.graphs = this.graphs.filter(graph => graph)
- this.drawAllGraph()
- }
- /**
- * @description Draw all the graphs in the render
- * @return {Undefined} Void
- */
- CRender.prototype.drawAllGraph = function () {
- this.clearArea()
- this.graphs.filter(graph => graph && graph.visible).forEach(graph => graph.drawProcessor(this, graph))
- }
- /**
- * @description Animate the graph whose animation queue is not empty
- * and the animationPause is equal to false
- * @return {Promise} Animation Promise
- */
- CRender.prototype.launchAnimation = function () {
- const { animationStatus } = this
- if (animationStatus) return
- this.animationStatus = true
- return new Promise(resolve => {
- animation.call(this, () => {
- this.animationStatus = false
- resolve()
- }, Date.now())
- })
- }
- /**
- * @description Try to animate every graph
- * @param {Function} callback Callback in animation end
- * @param {Number} timeStamp Time stamp of animation start
- * @return {Undefined} Void
- */
- function animation (callback, timeStamp) {
- const { graphs } = this
- if (!animationAble(graphs)) {
- callback()
- return
- }
- graphs.forEach(graph => graph.turnNextAnimationFrame(timeStamp))
- this.drawAllGraph()
- requestAnimationFrame(animation.bind(this, callback, timeStamp))
- }
- /**
- * @description Find if there are graph that can be animated
- * @param {[Graph]} graphs
- * @return {Boolean}
- */
- function animationAble (graphs) {
- return graphs.find(graph => !graph.animationPause && graph.animationFrameState.length)
- }
- /**
- * @description Handler of CRender mousedown event
- * @return {Undefined} Void
- */
- function mouseDown (e) {
- const { graphs } = this
- const hoverGraph = graphs.find(graph => graph.status === 'hover')
- if (!hoverGraph) return
- hoverGraph.status = 'active'
- }
- /**
- * @description Handler of CRender mousemove event
- * @return {Undefined} Void
- */
- function mouseMove (e) {
- const { offsetX, offsetY } = e
- const position = [offsetX, offsetY]
- const { graphs } = this
- const activeGraph = graphs.find(graph => (graph.status === 'active' || graph.status === 'drag'))
- if (activeGraph) {
- if (!activeGraph.drag) return
- if (typeof activeGraph.move !== 'function') {
- console.error('No move method is provided, cannot be dragged!')
- return
- }
- activeGraph.moveProcessor(e)
- activeGraph.status = 'drag'
- return
- }
- const hoverGraph = graphs.find(graph => graph.status === 'hover')
- const hoverAbleGraphs = graphs.filter(graph =>
- (graph.hover && (typeof graph.hoverCheck === 'function' || graph.hoverRect)))
- const hoveredGraph = hoverAbleGraphs.find(graph => graph.hoverCheckProcessor(position, graph))
- if (hoveredGraph) {
- document.body.style.cursor = hoveredGraph.style.hoverCursor
- } else {
- document.body.style.cursor = 'default'
- }
- let [hoverGraphMouseOuterIsFun, hoveredGraphMouseEnterIsFun] = [false, false]
- if (hoverGraph) hoverGraphMouseOuterIsFun = typeof hoverGraph.mouseOuter === 'function'
- if (hoveredGraph) hoveredGraphMouseEnterIsFun = typeof hoveredGraph.mouseEnter === 'function'
- if (!hoveredGraph && !hoverGraph) return
- if (!hoveredGraph && hoverGraph) {
- if (hoverGraphMouseOuterIsFun) hoverGraph.mouseOuter(e, hoverGraph)
- hoverGraph.status = 'static'
- return
- }
- if (hoveredGraph && hoveredGraph === hoverGraph) return
- if (hoveredGraph && !hoverGraph) {
- if (hoveredGraphMouseEnterIsFun) hoveredGraph.mouseEnter(e, hoveredGraph)
- hoveredGraph.status = 'hover'
- return
- }
- if (hoveredGraph && hoverGraph && hoveredGraph !== hoverGraph) {
- if (hoverGraphMouseOuterIsFun) hoverGraph.mouseOuter(e, hoverGraph)
- hoverGraph.status = 'static'
- if (hoveredGraphMouseEnterIsFun) hoveredGraph.mouseEnter(e, hoveredGraph)
- hoveredGraph.status = 'hover'
- }
- }
- /**
- * @description Handler of CRender mouseup event
- * @return {Undefined} Void
- */
- function mouseUp (e) {
- const { graphs } = this
- const activeGraph = graphs.find(graph => graph.status === 'active')
- const dragGraph = graphs.find(graph => graph.status === 'drag')
- if (activeGraph && typeof activeGraph.click === 'function') activeGraph.click(e, activeGraph)
- graphs.forEach(graph => graph && (graph.status = 'static'))
- if (activeGraph) activeGraph.status = 'hover'
- if (dragGraph) dragGraph.status = 'hover'
- }
- /**
- * @description Clone Graph
- * @param {Graph} graph The target to be cloned
- * @return {Graph} Cloned graph
- */
- CRender.prototype.clone = function (graph) {
- const style = graph.style.getStyle()
- let clonedGraph = { ...graph, style }
- delete clonedGraph.render
- clonedGraph = deepClone(clonedGraph, true)
- return this.add(clonedGraph)
- }
|