Commit 1e7ded21 authored by Sascha Herzinger's avatar Sascha Herzinger
Browse files

Getting rid of nasty canvas code by doing canvas -> image

parent 93b63e51
Pipeline #3433 passed with stages
in 3 minutes and 35 seconds
<template>
<chart v-on:resize="resize">
<control-panel class="fjs-control-panel">
<control-panel class="fjs-control-panel" name="Boxplot Panel">
<data-box class="fjs-data-box"
header="Numerical Variables"
dataType="numerical"
......@@ -109,11 +109,11 @@
:width="boxplotWidth"
:height="boxes[label].l_qrt - boxes[label].median">
</rect>
<svg-canvas name="fjs-canvas"
:z-index="1"
:data-label="label"
:height="padded.height"
:width="boxplotWidth / 2"/>
<image :href="dataUrls[label]"
:data-label="label"
:height="padded.height"
:width="boxplotWidth / 2">
</image>
<polyline class="fjs-kde"
:points="kdePolyPoints[label]"
v-if="params.showKDE">
......@@ -135,8 +135,8 @@
import deepFreeze from 'deep-freeze-strict'
import { truncateTextUntil } from '../mixins/utils'
import tooltip from '../directives/tooltip'
import SvgCanvas from '../components/SVGCanvas.vue'
import StateSaver from '../mixins/state-saver'
import getHDPICanvas from '../mixins/high-dpi-canvas'
export default {
name: 'boxplot',
data () {
......@@ -158,7 +158,8 @@
data: [],
statistics: {},
anova: {}
}
},
dataUrls: {}
}
},
computed: {
......@@ -194,6 +195,13 @@
labels () {
return Object.keys(this.results.statistics).sort()
},
canvas () {
const canvas = {}
this.labels.forEach(label => {
canvas[label] = getHDPICanvas(this.boxplotWidth / 2, this.padded.height)
})
return canvas
},
points () {
const points = {}
this.labels.forEach(label => {
......@@ -358,8 +366,8 @@
this.hasSetFilter = true
},
drawPoints () {
Object.keys(this.points).forEach(label => {
const canvas = this.$el.querySelector(`.fjs-canvas[data-label="${label}"]`)
this.labels.forEach(label => {
const canvas = this.canvas[label]
const ctx = canvas.getContext('2d')
ctx.clearRect(0, 0, canvas.width, canvas.height)
if (this.params.showData) {
......@@ -374,6 +382,8 @@
)
})
}
// we create new properties. We need to tell Vue that they are reactive
this.$set(this.dataUrls, label, canvas.toDataURL())
})
},
resize ({height, width}) {
......@@ -392,7 +402,6 @@
}
},
components: {
SvgCanvas,
ControlPanel,
DataBox,
Chart
......
......@@ -34,7 +34,6 @@
<svg :height="height" :width="width">
<g :transform="`translate(${margin.left}, ${margin.top})`">
<svg-canvas name="fjs-canvas" :width="padded.width" :height="padded.height"/>
<html2svg :right="padded.width">
<draggable>
<div class="fjs-legend">
......@@ -53,11 +52,12 @@
</div>
</draggable>
</html2svg>
<crosshair :width="padded.width" :height="padded.height"/>
<g class="fjs-corr-axis fjs-y-axis-2" :transform="`translate(${padded.width}, 0)`"></g>
<g class="fjs-corr-axis fjs-x-axis-2"></g>
<g class="fjs-corr-axis fjs-x-axis-1" :transform="`translate(0, ${padded.height})`"></g>
<g class="fjs-corr-axis fjs-y-axis-1"></g>
<crosshair :width="padded.width" :height="padded.height"/>
<image :href="dataUrl" :width="padded.width" :height="padded.height"></image>
<g class="fjs-brush"></g>
<text class="fjs-axis-label"
:x="padded.width / 2"
......@@ -108,10 +108,10 @@
import * as d3 from 'd3'
import tooltip from '../directives/tooltip.js'
import deepFreeze from 'deep-freeze-strict'
import SvgCanvas from '../components/SVGCanvas.vue'
import Crosshair from '../components/Crosshair.vue'
import Html2svg from '../components/HTML2SVG.vue'
import Draggable from '../components/Draggable.vue'
import getHDPICanvas from '../mixins/high-dpi-canvas'
export default {
name: 'correlation-analysis',
data () {
......@@ -146,7 +146,8 @@
data: []
},
selectedPoints: [],
hasSetFilter: false
hasSetFilter: false,
dataUrl: ''
}
},
computed: {
......@@ -181,6 +182,9 @@
const height = this.height - this.margin.top - this.margin.bottom
return {width, height}
},
canvas () {
return getHDPICanvas(this.padded.width, this.padded.height)
},
categories () {
return [...new Set(this.shownResults.data.map(d => d.category))]
},
......@@ -398,7 +402,6 @@
components: {
Draggable,
Html2svg,
SvgCanvas,
ControlPanel,
DataBox,
Chart,
......@@ -425,9 +428,7 @@
.catch(error => console.error(error))
},
drawPoints (points) {
const canvas = this.$el.querySelector('.fjs-canvas')
const ctx = canvas.getContext('2d')
ctx.clearRect(0, 0, canvas.width, canvas.height)
const ctx = this.canvas.getContext('2d')
points.forEach(d => {
ctx.beginPath()
ctx.fillStyle = this.categoryColors[this.categories.indexOf(d.category) % this.categoryColors.length]
......@@ -438,6 +439,7 @@
ctx.closePath()
ctx.fill()
})
this.dataUrl = this.canvas.toDataURL('image/png')
},
resize ({width, height}) {
this.width = width
......
<template>
<chart v-on:resize="resize">
<control-panel class="fjs-control-panel" name="Heat">
<control-panel class="fjs-control-panel" name="Heatmap Panel">
<data-box class="fjs-data-box"
header="Numerical Variables"
dataType="numerical_array"
......@@ -13,14 +13,14 @@
<fieldset>
<legend>Expression Level</legend>
<div>
<input type="radio" value="mean" v-model="rankingMethod">
<label>
<input type="radio" value="mean" v-model="rankingMethod">
Mean
</label>
</div>
<div>
<input type="radio" value="median" v-model="rankingMethod">
<label>
<input type="radio" value="median" v-model="rankingMethod">
Median
</label>
</div>
......@@ -28,8 +28,8 @@
<fieldset>
<legend>Expression Variability</legend>
<div>
<input type="radio" value="variance" v-model="rankingMethod">
<label>
<input type="radio" value="variance" v-model="rankingMethod">
Variance
</label>
</div>
......@@ -37,38 +37,38 @@
<fieldset>
<legend>Differential Expression</legend>
<div>
<input type="radio" value="logFC" v-model="rankingMethod">
<label>
<input type="radio" value="logFC" v-model="rankingMethod">
logFC
</label>
</div>
<div>
<input type="radio" value="t" v-model="rankingMethod">
<label>
<input type="radio" value="t" v-model="rankingMethod">
t
</label>
</div>
<div>
<input type="radio" value="F" v-model="rankingMethod">
<label>
<input type="radio" value="F" v-model="rankingMethod">
F
</label>
</div>
<div>
<input type="radio" value="B" v-model="rankingMethod">
<label>
<input type="radio" value="B" v-model="rankingMethod">
B
</label>
</div>
<div>
<input type="radio" value="P.Val" v-model="rankingMethod">
<label>
<input type="radio" value="P.Val" v-model="rankingMethod">
P.Value
</label>
</div>
<div>
<input type="radio" value="adj.P.Val" v-model="rankingMethod">
<label>
<input type="radio" value="adj.P.Val" v-model="rankingMethod">
adj.P.Value
</label>
</div>
......@@ -93,7 +93,7 @@
</div>
</fieldset>
<fieldset class="fjs-cluster-option-fieldset" v-if="cluster.algorithm == 'hclust'">
<fieldset class="fjs-cluster-option-fieldset" v-if="cluster.algorithm === 'hclust'">
<legend>Options</legend>
<div class="fjs-hclust-selects">
<select v-model="cluster.options.method">
......@@ -131,7 +131,7 @@
</div>
</fieldset>
<fieldset class="fjs-cluster-option-fieldset" v-if="cluster.algorithm == 'kmeans'">
<fieldset class="fjs-cluster-option-fieldset" v-if="cluster.algorithm === 'kmeans'">
<legend>Options</legend>
<div class="fjs-cluster-ranges">
<label>
......@@ -155,7 +155,7 @@
<svg :height="height" :width="width">
<g :transform="`translate(${margin.left}, ${margin.top})`">
<svg-canvas name="fjs-canvas" :width="padded.width" :height="padded.height"/>
<image :href="dataUrl" :width="padded.width" :height="padded.height"></image>
<rect class="fjs-sig-bar"
:x="bar.x"
:y="bar.y"
......@@ -180,7 +180,7 @@
import * as d3 from 'd3'
import tooltip from '../directives/tooltip.js'
import deepFreeze from 'deep-freeze-strict'
import SvgCanvas from '../components/SVGCanvas.vue'
import getHDPICanvas from '../mixins/high-dpi-canvas'
export default {
name: 'heatmap',
data () {
......@@ -211,7 +211,8 @@
results: {
data: {id: [], feature: [], value: [], zscore: []},
stats: {feature: []}
}
},
dataUrl: ''
}
},
computed: {
......@@ -264,6 +265,9 @@
height = height < 0 ? 0 : height
return { width, height }
},
canvas () {
return getHDPICanvas(this.padded.width, this.padded.height)
},
cols () {
let cols = []
if (this.cluster.results.cols.length) {
......@@ -447,14 +451,14 @@
this.numericArrayDataIds = ids
},
drawCells (cells) {
const canvas = this.$el.querySelector('.fjs-canvas')
const ctx = canvas.getContext('2d')
ctx.clearRect(0, 0, canvas.width, canvas.height)
const ctx = this.canvas.getContext('2d')
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
cells.forEach(d => {
ctx.beginPath()
ctx.fillStyle = d.fill
ctx.fillRect(d.x, d.y, d.width, d.height)
})
this.dataUrl = this.canvas.toDataURL()
}
},
watch: {
......@@ -480,7 +484,6 @@
}
},
components: {
SvgCanvas,
ControlPanel,
DataBox,
Chart
......
<template>
<chart v-on:resize="resize">
<control-panel class="fjs-control-panel">
<control-panel class="fjs-control-panel" name="PCA Panel">
<data-box class="fjs-data-box"
header="Numerical Variables"
dataType="numerical,numerical_array"
......@@ -38,7 +38,6 @@
<svg :width="width" :height="height">
<g :transform="`translate(${margin.left}, ${margin.top})`">
<svg-canvas name="fjs-canvas" :width="padded.width" :height="padded.height"/>
<html2svg :right="padded.width">
<draggable>
<div class="fjs-legend">
......@@ -55,12 +54,13 @@
</div>
</draggable>
</html2svg>
<crosshair :width="padded.width" :height="padded.height"/>
<g class="fjs-brush"></g>
<g class="fjs-axis fjs-y-axis-2" :transform="`translate(${padded.width}, 0)`"></g>
<g class="fjs-axis fjs-x-axis-2"></g>
<g class="fjs-axis fjs-x-axis-1" :transform="`translate(0, ${padded.height})`"></g>
<g class="fjs-axis fjs-y-axis-1"></g>
<crosshair :width="padded.width" :height="padded.height"/>
<image :href="dataUrls.main" :width="padded.width" :height="padded.height"></image>
<g class="fjs-brush"></g>
<text :x="padded.width / 2"
:y="- margin.top / 2"
text-anchor="middle"
......@@ -88,20 +88,20 @@
<g class="fjs-pc-distribution fjs-pc-x-distribution"
:transform="`translate(0, ${padded.height + margin.bottom / 2})`">
<line :x2="padded.width"></line>
<svg-canvas name="fjs-pc-x-distribution-canvas"
:y="-pointSize / 2"
:width="padded.width"
:height="pointSize">
</svg-canvas>
<image :href="dataUrls.xDist"
:y="-pointSize / 2"
:width="padded.width"
:height="pointSize">
</image>
</g>
<g class="fjs-pc-distribution fjs-pc-y-distribution"
:transform="`translate(${- margin.left / 2}, 0)`">
<line :y2="padded.height"></line>
<svg-canvas name="fjs-pc-y-distribution-canvas"
:x="-pointSize / 2"
:width="pointSize"
:height="padded.height">
</svg-canvas>
<image :href="dataUrls.yDist"
:x="-pointSize / 2"
:width="pointSize"
:height="padded.height">
</image>
</g>
</g>
</g>
......@@ -119,10 +119,10 @@
import * as d3 from 'd3'
import tooltip from '../directives/tooltip.js'
import deepFreeze from 'deep-freeze-strict'
import SvgCanvas from '../components/SVGCanvas.vue'
import Crosshair from '../components/Crosshair.vue'
import Html2svg from '../components/HTML2SVG.vue'
import Draggable from '../components/Draggable.vue'
import getHDPICanvas from '../mixins/high-dpi-canvas'
export default {
name: 'pca-analysis',
data () {
......@@ -154,6 +154,11 @@
hasSetFilter: false,
params: {
whiten: false
},
dataUrls: {
main: '',
xDist: '',
yDist: ''
}
}
},
......@@ -188,6 +193,13 @@
pointSize () {
return this.padded.width / 125
},
canvas () {
return {
main: getHDPICanvas(this.padded.width, this.padded.height),
xDist: getHDPICanvas(this.padded.width, this.pointSize),
yDist: getHDPICanvas(this.pointSize, this.padded.height)
}
},
scales () {
const x = d3.scaleLinear()
.domain((() => {
......@@ -350,7 +362,7 @@
.catch(error => console.error(error))
},
drawScatterPoints (points) {
const canvas = this.$el.querySelector('.fjs-canvas')
const canvas = this.canvas.main
const ctx = canvas.getContext('2d')
ctx.clearRect(0, 0, canvas.width, canvas.height)
points.forEach(d => {
......@@ -363,10 +375,11 @@
ctx.closePath()
ctx.fill()
})
this.dataUrls.main = canvas.toDataURL()
},
drawDistPoints (points) {
const xCanvas = this.$el.querySelector('.fjs-pc-x-distribution-canvas')
const yCanvas = this.$el.querySelector('.fjs-pc-y-distribution-canvas')
const xCanvas = this.canvas.xDist
const yCanvas = this.canvas.yDist
const xctx = xCanvas.getContext('2d')
const yctx = yCanvas.getContext('2d')
xctx.clearRect(0, 0, xCanvas.width, xCanvas.height)
......@@ -379,6 +392,8 @@
xctx.fillRect(point.x - this.pointSize / 2, 0, this.pointSize, this.pointSize)
yctx.fillRect(0, point.y - this.pointSize / 2, this.pointSize, this.pointSize)
})
this.dataUrls.xDist = xCanvas.toDataURL()
this.dataUrls.yDist = yCanvas.toDataURL()
},
resize ({height, width}) {
this.height = height
......@@ -392,7 +407,6 @@
}
},
components: {
SvgCanvas,
ControlPanel,
DataBox,
Chart,
......
<template>
<html2svg ref="html2svg" :left="x" :top="y" :z-index="zIndex">
<canvas ref="canvas" :class="name" v-bind="$attrs"></canvas>
</html2svg>
</template>
<script>
import Html2svg from '../components/HTML2SVG.vue'
export default {
name: 'svg-canvas',
props: {
x: {
type: Number,
default: 0,
required: false
},
y: {
type: Number,
default: 0,
required: false
},
height: {
type: Number,
required: true
},
width: {
type: Number,
required: true
},
name: {
type: String,
required: true
},
zIndex: {
type: Number,
default: -1,
required: false
}
},
computed: {
sizeChangeIndicator () {
return [this.width, this.height].join(' ')
}
},
watch: {
'sizeChangeIndicator': {
handler: function () {
this.makeHighDPICanvas()
this.$refs.html2svg.setPosition()
}
}
},
methods: {
makeHighDPICanvas () {
const scaleRatio = window.devicePixelRatio
const canvas = this.$refs.canvas
const ctx = canvas.getContext('2d')
// const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height)
canvas.width = this.width * scaleRatio
canvas.height = this.height * scaleRatio
canvas.style.width = this.width + 'px'
canvas.style.height = this.height + 'px'
ctx.setTransform(scaleRatio, 0, 0, scaleRatio, 0, 0)
// ctx.putImageData(imgData, 0, 0, 0, 0, canvas.width, canvas.height)
}
},
mounted () {
this.makeHighDPICanvas()
},
components: {
Html2svg
}
}
</script>
<style lang="sass" scoped>
canvas
position: fixed
</style>
/**
* Returns a canvas that takes the window pixel ratio into account.
* This is very important on devices with ratios > 1 to get crisp drawings.
* @param width: Width of the canvas.
* @param height: Height of the canvas.
* @returns {HTMLCanvasElement}
*/
export default function (width, height) {
const scaleRatio = window.devicePixelRatio
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
canvas.width = width * scaleRatio
canvas.height = height * scaleRatio
canvas.style.width = width + 'px'
canvas.style.height = height + 'px'
ctx.setTransform(scaleRatio, 0, 0, scaleRatio, 0, 0)
return canvas
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment