How to create a video component with vue-konva? - vue.js

With konva.js you can create video components on your canvas. Here you can see the example in the docs (https://konvajs.org/docs/sandbox/Video_On_Canvas.html).
Now I'm using vue-konva and there is no component to create a video on the canvas. You need to do it with the v-image component but I am not able to make it work. Is it possible with vue-konva?

Here's a fiddle of a video working with v-image.
It's a working version of this codesandbox I found.
const width = window.innerWidth;
const height = window.innerHeight;
new Vue({
el: "#app",
data() {
return {
stageSize: {
width: width,
height: height
},
video: null,
animation: null
};
},
computed: {
imageConfig() {
return {
image: this.video,
x: 0,
y: 0,
width: 320,
height: 180
};
}
},
methods: {
play() {
this.video.play();
this.animation.start();
},
pause() {
this.video.pause();
this.animation.stop();
}
},
mounted() {
this.video = document.createElement("video");
this.video.src =
"https://upload.wikimedia.org/wikipedia/commons/transcoded/c/c4/Physicsworks.ogv/Physicsworks.ogv.240p.vp9.webm";
this.layer = this.$refs.image.getStage().getLayer();
this.animation = new Konva.Animation(() => {
// do nothing, animation just need to update the layer
}, this.layer);
this.video.addEventListener("canplay", () => {
this.play();
});
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/4.2.2/konva.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-konva#2.1.1/umd/vue-konva.min.js"></script>
<div id="app">
<button #click="play">Play</button>
<button #click="pause">Pause</button>
<v-stage ref="stage" :config="stageSize">
<v-layer ref="layer">
<v-image ref="image" :config="imageConfig" />
</v-layer>
</v-stage>
</div>

Related

How to calculate table height in watch in vue

I have a table and I give a ref to it.
<table ref="table"></table>
I want to calculate the height of this table in watcher:
watch: {
buttonClicked: {
immediate: true,
handler() {
this.$nextTick(() => {
const tableElement = this.$refs.table.$el;
const tableOffsetTop = tableElement.getBoundingClientRect().top;
});
},
},
}
But I am getting an error: Uncaught TypeError: Cannot read properties of undefined (reading '$el')
I tried ti fix it with this.$nextTick but this time I cannot calculate it right.
How can I fix it?
Try without $el:
const app = Vue.createApp({
data() {
return {
height: null
}
},
watch: {
buttonClicked: {
handler() {
this.$nextTick(() => {
const tableElement = this.$refs.table;
const tableOffsetTop = tableElement.getBoundingClientRect().top;
this.height = tableElement.getBoundingClientRect().bottom -tableElement.getBoundingClientRect().top
});
},
immediate: true,
},
}
})
app.mount('#demo')
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="demo">
<table ref="table"><tr><td>table</td></tr></table>
height: {{ height }}
</div>

Selecting a field in an ArcGis layer/map in a Nuxt application result in adding legend colorSlider on top of each other in the same div

I am using ArcGis in a Nuxt application. I have got a map with a feature layer (hosted on ArcGis) and a legend with a color slider in the top-right corner. The user can visualise different fields from the layer. For each field selected a new renderer is generated and therefore a new colorSlider. My problem is that every time the user select a new field, a new colorSlider is added above the previous one and I end up with three coloSliders in the legend. How can I fix that ?? I tried to destroy the previous colorSlider when I select a new field but it seems to destroy the div which contains the slider and then I have no slider at all anymore... This is the code =>
<template>
<div>
<div id="viewDiv"></div>
<div id="legend"></div>
<div id="containerDiv" class="esri-widget">
<span id="title" class="esri-widget">impact legend</span>
<div id="slider" ref="sliderr"></div>
</div>
</div>
</template>
<script>
import Map from '#arcgis/core/Map'
import MapView from '#arcgis/core/views/MapView'
import esriConfig from '#arcgis/core/config'
import FeatureLayer from '#arcgis/core/layers/FeatureLayer'
import * as colorRendererCreator from '#arcgis/core/smartMapping/renderers/color'
import ColorSlider from '#arcgis/core/widgets/smartMapping/ColorSlider'
export default {
props: {
selectedTab: {
type: Number,
default: 1,
},
},
data() {
return {
url: 'https://blablabla',
countries:
'https://blablabla',
projectLyr: undefined,
countryLyr: undefined,
map: new Map({ basemap: 'osm-light-gray' }),
view: undefined,
fieldName: '',
renderer: {},
filter: '',
rendererResult: undefined,
colorSlider: undefined,
}
},
mounted() {
esriConfig.apiKey =
'myApiKey'
this.projectLyr = new FeatureLayer({
url: this.url,
outFields: ['*'],
})
this.countryLyr = new FeatureLayer({
url: this.countries,
outFields: ['*'],
})
this.view = new MapView({
map: this.map,
center: [15, 50],
zoom: 6,
container: 'viewDiv',
})
this.updateLayer({ layer: this.projectLyr, value: 'Impact_PA_area' })
this.$nuxt.$on('filter-selected', this.updateLayer)
},
beforeDestroy() {
this.$nuxt.$off('tab-selected')
this.$nuxt.$off('filter-selected')
},
methods: {
generateRenderer(lyr) {
const colorParams = {
layer: lyr.layer,
field: `${lyr.field}`,
view: this.view,
theme: 'above-and-below',
}
colorRendererCreator
.createContinuousRenderer(colorParams)
.then((response) => {
// Set the renderer to the layer and add it to the map
this.rendererResult = response
lyr.layer.renderer = this.rendererResult.renderer
})
.then(() => {
// Construct a color slider from the result of smart mapping renderer
this.colorSlider = ColorSlider.fromRendererResult(this.rendererResult)
this.colorSlider.container = 'slider'
this.colorSlider.primaryHandleEnabled = true
this.colorSlider.viewModel.precision = 1
this.view.ui.add('containerDiv', 'top-right')
function changeEventHandler() {
const renderer = lyr.layer.renderer.clone()
const colorVariable = renderer.visualVariables[0].clone()
const outlineVariable = renderer.visualVariables[1]
colorVariable.stops = this.colorSlider.stops
renderer.visualVariables = [colorVariable, outlineVariable]
lyr.layer.renderer = renderer
}
this.colorSlider.on(
['thumb-change', 'thumb-drag', 'min-change', 'max-change'],
changeEventHandler
)
})
.catch((error) => {
console.error('Error: ', error)
})
},
filtering(value) {
if (value.value.isFilter) {
this.filter = `${value.value.value}`
this.projectLyr.definitionExpression = this.filter
} else {
this.projectLyr.definitionExpression = `${value.value.value} AND IS NOT NULL`
if (this.filter !== '') {
this.projectLyr.definitionExpression = this.filter
}
value.isCountry
? this.generateRenderer({
layer: this.countryLyr,
field: value.value.value,
})
: this.generateRenderer({
layer: this.projectLyr,
field: value.value.value,
})
}
},
updateLayer(value) {
this.$nextTick(() => {
if (this.selectedTab === 0) {
this.map.remove(this.projectLyr)
this.map.add(this.countryLyr)
this.filtering({ value, isCountry: true })
} else {
this.map.remove(this.countryLyr)
this.map.add(this.projectLyr)
this.filtering({ value, isCountry: false })
}
})
},
},
}
</script>
<style scoped>
#import 'https://js.arcgis.com/4.23/#arcgis/core/assets/esri/themes/light/main.css';
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
#containerDiv {
background-color: white;
padding: 3px;
text-align: center;
min-width: 260px;
}
</style>
I think you can just update the ColorSlider with the new ContinuousRendererResult data instead of trying to destroy/recreate. In a similar way you create it, use updateFromRendererResult method to update it (ArcGIS JS API - ColorSlider).

How can I connect Three.js correctly to the Vue component?

I tried to connect Three.js is the standard way to the Vue component, but for some reason this method turned out to be non-working for me.. The component connects, but does not display anything. What could be the problem?
Here is my test component:
<template>
<div id="container"></div>
</template>
<script>
import * as THREE from 'three'
export default {
name: 'ThreeTest',
data() {
return {
}
},
methods: {
init: function() {
let container = document.getElementById('container');
this.scene = new THREE.Scene()
this.camera = new THREE.PerspectiveCamera(
75,
container.innerWidth / container.innerHeight,
0.1,
1000
)
this.renderer = new THREE.WebGLRenderer()
this.renderer.setSize(container.innerWidth, container.innerHeight)
document.body.appendChild(this.renderer.domElement)
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
this.cube = new THREE.Mesh(geometry, material)
this.scene.add(this.cube)
this.camera.position.z = 5
},
animate: function() {
requestAnimationFrame(this.animate)
this.cube.rotation.x += 0.01
this.cube.rotation.y += 0.01
this.renderer.render(this.scene, this.camera)
}
},
mounted() {
this.init()
this.animate()
}
}
</script>
<style scoped>
#container {
width: 500px;
height: 500px;
}
</style>

How do i have a single Chart.vue component instead of different chart components and then display charts from it by changing id of the API

I am displaying four charts, so i have created four different chart components, but all the components are same but they differ in API id.
In order to make code effective, i want to make a single Chart.vue component and then display the chart in any other component by their respective ID.
Here is one of my chart component:
<template>
<div class="chart-container" style="position: relative; height: 25vh; width:100%;">
<canvas id="DisplayChart" ></canvas>
</div>
</template>
<script>
import moment from 'moment'
export default {
name: 'Chart',
async mounted () {
await this.$http.get('/farfrom/chart/3') //Here 3 is the ID of the chart.
.then((response) => {
const result = response.data
const ctx = document.getElementById('DisplayChart').getContext('2d')
const Chart_data = []
for (let i = 0; i < result.date.length; i++) {
Chart_data.push({
x_axis: moment(result.date[i], 'X').toDate(),
y_axis: result.challenge[i]
})
}
let myChart
if (myChart !== undefined) {
myChart.destroy()
}
myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [
{
label: 'Chart_from_API',
data: Chart_data,
]
},
options: {
lineTension: 0,
maintainAspectRatio: false,
scales: {
yAxes: [
{
scaleLabel: {
display: false
},
ticks: {
beginAtZero: true,
// eslint-disable-next-line no-unused-vars
callback (value) {
return `${value }k` // y-axis value will append k to it
}
}
}
],
xAxes: [
{
type: 'time',
time: {
unit: 'month'
},
scaleLabel: {
display: true
}
}
]
}
}
})
})
.catch((error) => {
console.log(error)
})
}
}
</script>
i have imported this in another component like:
<template>
<div class="chart-container">
<Chart></Chart>
</div>
</template>
<script>
import Chart from 'Chart.vue'
....
</script>
Here i have shown the example of one of my chart among 4 charts, my other charts are also same only change is in the id of the API.
Now as i said i want to know how i can make one Single Chart.vue with some variable to API Id and use it in any component by their respective id. Please do help me in this.
Objective: I want to have a single Chart.vue component instead of 4 charts, and if i want to use any chart i should be able to use their respective id.
You could use props to pass data to a child component.
https://v2.vuejs.org/v2/guide/components-props.html
Example
App.vue
<template>
<div id="app">
<Chart :id="chartId" />
</div>
</template>
<script>
import Chart from "#/components/Chart.vue";
// #/ means src/
export default {
name: "App",
components: {
Chart,
},
data: () => ({
chartId: 2,
}),
};
</script>
Chart.vue
<template>
<div>
{{ id }}
</div>
</template>
<script>
export default {
name: "Chart",
props: {
id: {
type: Number,
required: true,
},
},
mounted() {
const apiURL = `/farfrom/chart/${this.id}`;
// API call
}
};
</script>

Vuex Store + PixiJs using Mounted to render too soon?

If I edit on the code then suddenly the connections will render in the browser, but on initial load in the browser they do no display and its appear the array configConnections is not ready / empty, so do I need to call drawPixi() later somehow ? maybe use nexttick? or am I missing something here should it be async ?
<template>
<div class="connections">
<canvas id="pixi"></canvas>
</div>
</template>
<script>
import { mapState } from 'vuex'
import * as PIXI from 'pixi.js'
export default {
name: 'ConnectionsLayer',
computed: mapState({
configConnections: (state) => state.configConnections,
}),
methods: {
drawPixi() {
var i
var canvas = document.getElementById('pixi')
const app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
antialias: true,
transparent: true,
view: canvas,
})
let graphics = new PIXI.Graphics()
graphics.lineStyle(8, 0xcab6ff)
for (i = 0; i < Object.keys(this.configConnections).length; i++) {
//start
graphics.moveTo(
this.configConnections[i].x_pos_start,
this.configConnections[i].y_pos_start
)
//end
graphics.lineTo(
this.configConnections[i].x_pos_end,
this.configConnections[i].y_pos_end
)
}
app.stage.addChild(graphics)
},
},
mounted() {
this.drawPixi()
},
}
</script>
<div class="connections">
<canvas ref="pixi" id="pixi"></canvas>
</div>
</template>
<script>
import { mapState } from 'vuex'
import * as PIXI from 'pixi.js'
export default {
name: 'ConnectionsLayer',
computed: mapState({
configConnections: (state) => state.configConnections,
}),
watch: {
configConnections: {
deep: true,
handler() {
this.drawPixi()
},
},
},
methods: {
drawPixi() {
var i
this.canvas = this.$refs.pixi
const stage = this.PIXIApp.stage
let graphics = new PIXI.Graphics()
graphics.lineStyle(8, 0xcab6ff)
for (i = 0; i < Object.keys(this.configConnections).length; i++) {
//start
graphics.moveTo(
this.configConnections[i].x_pos_start,
this.configConnections[i].y_pos_start
)
//end
graphics.lineTo(
this.configConnections[i].x_pos_end,
this.configConnections[i].y_pos_end
)
}
for (var j = stage.children.length - 1; j >= 0; j--) {
stage.removeChild(stage.children[j])
}
stage.addChild(graphics)
},
},
mounted() {
const canvas = this.$refs.pixi
this.PIXIApp = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
antialias: true,
transparent: true,
view: canvas,
})
this.drawPixi()
},
}