Does Vue-Konva support GLSL? - vue.js

Currently I'm using Vue-Konva.js to help me build a 2d editor for panorama.
Does konva.js support GLSL code for apply image effect (ex: stereographic projection)?
Maybe like this format.
KonvaScens.vue
<template>
<v-stage :config="configKonva">
<v-layer>
<v-image :config="configImage"></v-image>
</v-layer>
</v-stage>
</template>
<script>
import {myVertexShader, myFragmentShader} from 'imageShader'
export default {
data () {
return {
testImg: new Image(100, 100),
configKonva: {
width: 500,
height: 500
}
}
},
computed: {
configImage () {
return {
x: 20,
y: 20,
image: this.testImg,
width: 500,
height: 500,
vertexShader: myVertexShader,
fragmentShader: myFragmentShader
}
}
},
mounted () {
this.testImg.src = "https://konvajs.github.io/assets/lion.png"
}
}
</script>

No, vue-konva doesn't support GLSL. GLSL can be used in webgl canvas context. But Konva uses only 2d context.

Related

Binding A Media Feed/ Stream to Video Element - Vue 3 Composition API

I am trying to bind a camera feed to a video element using Vue 3, but I can not seem to even get a basic example working. Can someone tell me what I am missing?
import { ref, reactive } from 'vue'
let stream: any = reactive({})
let constraints = {
audio: false,
video: {
width: { min: 1024, ideal: 1280, max: 1920 },
height: { min: 576, ideal: 720, max: 1080 },
facingMode: 'environment'
}
}
stream = await navigator.mediaDevices.getUserMedia(constraints)
Then in my template I have tried both of the following ways to bind
<video :srcObject.prop="stream" width="300" height="200" autoplay></video>
<video :srcObject.prope.camel="stream" autoplay playsinline></video>
Your code overwrites stream with the result of getUserMedia(), which effectively removes the original reactive() instance from the component's context.
Solution
Make stream a ref instead of reactive, and set its value property to the result of getUserMedia().
Also, to ensure the user's resources are activated only when necessary, call getUserMedia() in the component's mounted hook, and stop the stream in the beforeUnmount hook.
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
const stream = ref(null)
const constraints = {
audio: false,
video: {
width: { min: 1024, ideal: 1280, max: 1920 },
height: { min: 576, ideal: 720, max: 1080 },
facingMode: 'environment',
},
}
onMounted(async () => {
stream.value = await navigator.mediaDevices.getUserMedia(constraints)
})
onBeforeUnmount(() => {
stream.value.getTracks().forEach(track => track.stop())
})
</script>
<template>
<video :srcObject="stream" width="300" height="200" autoplay></video>
</template>
demo

Using the vue-konva with nuxtjs fails with various error

I followed the documentation and Github I did the following steps:
install vue-konva and konva and canvas using npm install vue-konva konva --save and npm install canvas --save.
Created vuekonva.js under plugins folder with below content:
import Vue from 'vue'
import VueKonva from 'vue-konva'
Vue.use(VueKonva)
Added plugins: [ "~/plugins/vuekonva"], under nuxt.config.js
I tried adding under nuxt-config.js but still no luck:
build: {
standalone: true
},
Created a page under pages folder and added code from documenation:
<template>
<div>
<v-stage ref="stage" :config="stageSize">
<v-layer>
<v-text :config="{ text: 'Some text on canvas', fontSize: 15 }" />
<v-rect
:config="{
x: 20,
y: 50,
width: 100,
height: 100,
fill: 'red',
shadowBlur: 10,
}"
/>
<v-circle
:config="{
x: 200,
y: 100,
radius: 50,
fill: 'green',
}"
/>
<v-line
:config="{
x: 20,
y: 200,
points: [0, 0, 100, 0, 100, 100],
tension: 0.5,
closed: true,
stroke: 'black',
fillLinearGradientStartPoint: { x: -50, y: -50 },
fillLinearGradientEndPoint: { x: 50, y: 50 },
fillLinearGradientColorStops: [0, 'red', 1, 'yellow'],
}"
/>
</v-layer>
<v-layer ref="dragLayer" />
</v-stage>
</div>
</template>
<script>
export default {
data () {
return {
stageSize: {
width,
height
}
}
},
mounted () {
if (process.browser) {
this.stageSize.width = window.innerWidth
this.stageSize.height = window.innerHeight
}
}
}
</script>
I get the error:
Must use import to load ES Module:
I tried without plugins and it is throwing the error:
vue.runtime.esm.js:620 [Vue warn]: Unknown custom element: <v-stage> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
found in
Not understanding whats the issue please help.
According to Nuxt documentation some plugins export an ES6 module. I think this is the case for konva node module. I followed the steps you mentioned above. But in the nuxt.config.js file I configured the plugin as follow:
plugins: [
{ src: "~/plugins/vuekonva", mode: 'client' }
],
build: {
transpile: ['konva']
},
After that I replaced the code of your page with the code of konvajs as follows:
<template>
<v-stage :config="configKonva">
<v-layer>
<v-circle :config="configCircle"></v-circle>
</v-layer>
</v-stage>
</template>
<script>
export default {
data() {
return {
configKonva: {
width: 200,
height: 200
},
configCircle: {
x: 100,
y: 100,
radius: 70,
fill: "red",
stroke: "black",
strokeWidth: 4
}
};
}
};
</script>
That is working for me when I link to the page with nuxt-link. but if I refresh the page I get some errors that is maybe for SSR. I am not sure but if you read this documentation you maybe could solve the problem for SSR.

How to animate svg elements in a for loop with vue?

working with vue-konva (svg based canvas library) right now. I'm trying to animate all shapes which are defined by a v-loop. While trying to use the Konva Animation library functions, I'm getting "Cannot read property 'getNode' of undefined" error. I'm assuming this is due to the fact that a ref has to have one specific element and not be adressed inside a v-for loop. How can I simultaneaously animate all the polygons?
SVG / canvas element:
<v-regular-polygon
v-for="el in results"
ref="hexagon"
:key="el.index"
:config="{
x: 200 * Math.abs(el.land),
y: 200,
sides: 6,
radius: 20,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
}"
/>
function responsible for animation
mounted() {
this.fetchTemperature()
const vm = this
const amplitude = 100
const period = 5000
// in ms
const centerX = vm.$refs.stage.getNode().getWidth() / 2
const hexagon = this.$refs.hexagon.getNode()
// example of Konva.Animation
const anim = new Konva.Animation(function (frame) {
hexagon.setX(amplitude * Math.sin((frame.time * 2 * Math.PI) / period) + centerX)
}, hexagon.getLayer())
anim.start()
},
You can set a unique ref to every polygon by adding an index.
<v-regular-polygon
v-for="(el, i) in results"
:ref="`hexagon_${i}`"
...
Here's an example:
<template>
<div id="app">
<v-stage :config="configKonva">
<v-layer>
<v-circle
v-for="(item, i) in items"
:config="item"
:key="i"
:ref="`circle_${i}`"
></v-circle>
</v-layer>
</v-stage>
</div>
</template>
<script>
import Konva from "konva";
export default {
name: "App",
data() {
return {
items: [
{
x: 30,
y: 100,
radius: 20,
fill: "red",
stroke: "black",
strokeWidth: 2,
},
{
x: 100,
y: 100,
radius: 20,
fill: "red",
stroke: "black",
strokeWidth: 2,
},
],
configKonva: {
width: 200,
height: 200,
},
};
},
mounted() {
for (let i = 0; i < this.items.length; i++) {
const node = this.$refs[`circle_${i}`][0].getNode();
const period = 1000;
const amplitude = 10;
const anim = new Konva.Animation((frame) => {
node.setX(
amplitude * Math.sin((frame.time * 2 * Math.PI) / period) +
this.items[i].x
);
}, node.getLayer());
anim.start();
}
},
};
</script>
Codesandbox

Computed styles not applied during leave transition

When an element has a computed style, the style changes are not applied if the element is going through a leave transition:
new Vue({
el: "#app",
data: {
selected: 1,
items: [{
color: 'red'
},
{
color: 'blue'
},
{
color: 'green'
},
],
tweened: {
height: 50,
},
},
computed: {
divStyles() {
return {
height: this.tweened.height + 'px',
background: this.displayed.color,
'margin-left': this.selected * 100 + 'px',
width: '100px',
}
},
displayed() {
return this.items[this.selected - 1]
}
},
watch: {
selected(newVal) {
function animate() {
if (TWEEN.update()) {
requestAnimationFrame(animate)
}
}
new TWEEN.Tween(this.tweened)
.to({
height: newVal * 50
}, 2000)
.easing(TWEEN.Easing.Quadratic.InOut)
.start()
animate()
}
},
methods: {
toggle: function(todo) {
todo.done = !todo.done
}
}
})
.colored-div {
opacity: 1;
position: absolute;
}
.switcher-leave-to,
.switcher-enter {
opacity: 0;
}
.switcher-enter-to,
.switcher-leave {
opacity: 1;
}
.switcher-leave-active,
.switcher-enter-active {
transition: opacity 5s linear;
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.21/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/16.3.5/Tween.min.js"></script>
<div id="app">
<button #click="selected--" :disabled="selected <= 1">
Previous
</button>
<button #click="selected++" :disabled="selected >= 3">
Next
</button>
<span>Selected: {{selected}}</span>
<transition name="switcher">
<div v-for="(item, index) in items" v-if="index + 1 === selected" :key="index" :style="divStyles" class="colored-div" />
</transition>
</div>
https://jsfiddle.net/64syzru5/12/
I would expect the leaving element to continue resizing as it fades out, but it doesn't. What can be done to have the computed styles applied to the leaving element during the leave-active transition?
Since you're using CSS for the transitions, Javascript doesn't execute at each intermediate step. That's a good thing for performance, but it means that the computed properties aren't recomputed. As best as I can tell, though, you're just trying to animate the height. That's easily accomplished in pure CSS. Use a before-leave hook to set it to an initial value via an inline style or CSS variable, and then remove that property in the after-leave hook.
More to the point, though, it looks like your application might be more suitable for a transition-group instead of a simple transition.

Is it possible to print a chart with vue-chartjs?

I am using vue-chartjs to render graphs on a webapp. I know you can print charts if you are using the original library. However I have no idea on how to do it with the vue version of the library.
I have my charts variable on an external charts.js file
import {Bar, mixins } from 'vue-chartjs'
Chart.defaults.global
let chartOptions = Chart.defaults.global;
const { reactiveProp } = mixins
export default {
extends: Bar,
mixins: [reactiveProp],
props: ['options'],
mounted () {
let that = this;
that.chartOptions = {
scales: {
yAxes: [{
ticks: {
suggestedMin: 0,
fontFamily: "'Overpass_Mono', 'Monaco', monospace",
fontColor: "rgba(254, 255, 248, 0.5)"
},
gridLines: {
color: 'rgba(255, 80, 248, 0.08)',
zeroLineColor: 'rgb(168, 119, 181)',
zeroLineWidth: 2
},
}],
xAxes: [{
ticks: {
suggestedMin: 0,
fontColor: "rgb(168, 119, 181)"
},
gridLines: {
color: 'rgba(255, 80, 248, 0.08)',
zeroLineColor: 'transparent',
}
}],
},
legend: {
labels: {
fontColor: 'rgb(168, 119, 181)',
}
}
},
this.renderChart(this.chartData, that.chartOptions)
}
}
Then on my component template I have:
<template>
<div class="report">
<charts v-if="todaySelected"
:chart-id="'total_visits_chart_bar'"
:height="chartsHeight"
:options="chartOptions"
:chart-data="datacollection"
></charts>
<div v-if="todaySelected">
<button #click="printChart(charts)">Print chart</button>
</div>
</template>
<script>
import charts from './chart_0.js'
components: {
charts,
},
data() {
return{
datacollection: {"datasets":[{"label":"Entries Today","data":[15,15,15,0]},{"label":"Currently Inside","data":[2,2,2,0]}],"labels":[]}
}
}.
methods: {
printChart(charts) {
charts.print();
},
}
</script>
Any help would be appreciated.
The answer is: Yes, it is. Your print method in the components' script could be:
methods:{
printChart() {
var canvasEle = document.getElementById('total_visits_chart_bar');
var win = window.open('', 'Print', 'height=600,width=800');
win.document.write("<br><img src='" + canvasEle.toDataURL() + "' />");
setTimeout(function(){ //giving it 200 milliseconds time to load
win.document.close();
win.focus()
win.print();
win.location.reload()
}, 200);
},
}
You can also add this to your component's style:
#media print{
#page {
size: landscape
}
}
vue-chartjs is based on chart.js and not canvas.js, thus it does not have a "build-in" way of printing.
You have to do it with some custom logic and the native javascript printing functions.
You can however grab the canvas element inside your chart component and generate for example an image and then print that image.
It will get a bit tricky, because you only have access to the canvas inside your chart component. So you will need to maybe wait for an event or prop to trigger the toDataURL call and then emit the image to your parent component where you can print it. If you want to trigger the print in your parent component.
methods: {
print () {
// grab the canvas and generate an image
let image = this.$refs.canvas.toDataURL('image/png')
// Emits an event with the image
this.$emit('chart:print', image)
}
}
In your parent component:
<template>
<your-chart #chart:print="handlePrint"
<template/>
....
...
methods: {
handlePrint(image) {
const win = window.open('', 'Print', 'height=600, width=800')
win.document.write(`<br><img src='${image}' />`)
win.print()
win.close()
}
}
It seems like the library is based on chartjs not canvasjs https://www.chartjs.org/docs/latest/ you might want to look into how to print a window Quick Print HTML5 Canvas, and remember you have access to the canvas element where your graph is drawn:
methods: {
printChart() {
const canvasEle = this.$el.querySelector('canvas');
//now your chart image is on canvasEle
},
}
If you are not against using export to pdf format, you can implement this task using jsPDF library, for example:
<template>
<div class="report">
<charts v-if="todaySelected"
:chart-id="'total_visits_chart_bar'"
:height="chartsHeight"
:options="chartOptions"
:chart-data="datacollection"
></charts>
</div>
</template>
<script>
import jsPDF from 'jspdf'; //for PDF printing
methods: {
pdfThatThing : function(){
//Default export is a4 paper, portrait, using milimeters for units
let pdfName = 'test';
var doc = new jsPDF();
doc.text("Header", 20, 20); //at x,y at def.units 2cm
//chart element
let canvasEle = document.getElementById('total_visits_chart_bar');
let chartURL = canvasEle.toDataURL(); //transform path
//a4 page is 209mm, adds at 4cm top, 2cm left, for 15cm in size
doc.addImage(chartURL, 'PNG', 20, 40, 150, 150 )
doc.save(pdfName + '.pdf');
},
}
</script>
There is also option to auto show print dialog in pdf viewer:
doc.autoPrint({variant: 'non-conform'})