How to draw shape from path in vue konva? - vue.js

I am trying to use konvajs inside vue. I want to draw an object from path data, but i don't know how to do it. My main purpose is to get the path data from server, but first i would like to see some drawing in action.
Thank you!
All help appreciated.
<div>
<v-container>
<v-layout align-end justify-center row fill-height>
<v-flex xs12>
<v-stage :config="configKonva">
<v-layer>
<v-shape :config="configShape"/>
</v-layer>
</v-stage>
</v-flex>
</v-layout>
</v-container>
</div>
</template>
<script>
export default {
data() {
return {
configShape: {},
configKonva: {
width: 200,
height: 200
}
};
},
methods: {},
mounted() {
this.configShape = new Konva.Path({
fill: "#00D2FF",
stroke: "black",
strokeWidth: 4,
data: "m0,0L100,100L0,100L0,0",
sceneFunc: function(context, shape) {
// special Konva.js method
context.fillStrokeShape(shape);
}
});
}
};
</script>```

It is not recommended to use sceneFunc for built-in shapes (such as Konva.Path).
Take a look into shapes tutorial for vue-konva https://konvajs.org/docs/vue/Shapes.html.
If you want to create a Konva.Path you need to use v-path component:
<v-path
:config="{
x: 200,
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4,
data: 'm0,0L100,100L0,100L0,0',
}"
/>
Demo: https://codesandbox.io/s/32xxoon18p
You you want to have full control of drawing you can use custom shapes: https://konvajs.org/docs/vue/Custom_Shape.html

I could make it work. I don't think, that this is the correct way, but now at least it's working
mounted() {
this.configShape = new Konva.Path({
fill: "#00D2FF",
stroke: "black",
strokeWidth: 4,
data: "m0,0L100,100L0,100L0,0",
sceneFunc: function(context, shape) {
let arr = shape.attrs.dataArray;
context.beginPath();
for (var i = 0; i < arr.length; i++) {
if (arr[i].command == "M") {
context.moveTo(arr[i].points[0],arr[i].points[1]);
} else if (arr[i].command == "L") {
context.lineTo(arr[i].points[0],arr[i].points[1]);
}
}
context.closePath();
// special Konva.js method
context.fillStrokeShape(shape);
}
});
}

Related

How can I change the backgroundColor of a row in a beufy datatable with a given color for each row?

I'm new to web technologies and I'm stuck this this problem: I would like to be able to change the color of a row dynamically.
I tried different things with my knowledge and none worked well. I have a json with information for the table with the color code inside (ex: #D465F2). I wasn't able to find a way to color the entire row in one time dynamically. I can do it with predetermined css but it's not what I need.
The only way it worked is this one:
Template:
<template>
<div>
<p class="title">logs</p>
<b-table
:data="loginfo"
:sticky-header="stickyHeaders"
height="1200px"
:row-class="() => 'relative'"
>
<b-table-column v-slot="props" field="log_id" label="color">
<div class="colored-cell" :style="{ backgroundColor: props.row.color }">
<p>{{ props.row.color }}</p>
</div>
</b-table-column>
</b-table>
</div>
</template>
Script:
<script>
import axios from 'axios'
export default {
props: {
id: Number,
state: Number,
},
data() {
return {
loginfo: [],
stickyHeaders: true,
dateSearchable: false,
}
},
watch: {
id(newId) {
this.newLog()
},
},
methods: {
async newLog() {
await axios
.get('http://127.0.0.2:5001/api/log?sequence=' + this.id)
.then((response) => (this.loginfo = response.data.data))
},
},
}
Exemple of json send to the script:
"data": [
{
"color": "#15986E",
"log_id": 25,
"logged_at": "2022-05-17 16:27:00.015",
},
and this is my css:
.relative {
position: relative;
}
.colored-cell {
position: absolute;
top: 0;
left: 0;
padding: 8px 12px;
}
With this code I color each cell one by one but it makes the table unreadable and all broken. Everything is on top of each other. TableBroken Screen
If anyone have the solution to make the table looks great again/color row dynamically or anything that can help me, another lib which allows me to change the row color dynamically.
Just use a td-attrs prop as documented
<b-table>
:data="loginfo"
:sticky-header="stickyHeaders"
height="1200px"
>
<b-table-column v-slot="props" field="log_id" label="color" :td-attrs="columnTdAttrs">
{{ props.row.color }}
</b-table-column>
</b-table>
</div>
export default {
methods: {
columnTdAttrs(row, column) {
return {
style: {
backgroundColor: row.color
}
}
}
},
}

Vue Overlay for specific card

I am using Vue with Vuetify and I used a for loop to render 8 different cards. Each card has its own overlay which shows its own image when clicked, however when I click one card, all of the overlays get set off instead of the overlay for just the specific card. How should I go about doing this? Would I have to add an ID for each card?
<script>
export default {
data: () => ({
arts: [
require("#/assets/art1.jpg"),
require("#/assets/art2.jpg"),
require("#/assets/art3.jpg"),
require("#/assets/art4.jpeg"),
require("#/assets/art5.jpg"),
require("#/assets/art6.jpg"),
require("#/assets/art7.jpg"),
require("#/assets/art8.jpg")
],
absolute: true,
opacity: 1,
overlay: false,
})
};
</script>
<template>
<div style="width: 100%; height: 100%">
<v-container fluid style="height:100%; background-color:#fc8276; width:25%; float:left"></v-container>
<v-container style=" height: 100%; width: 75%; float:right" fluid>
<h1>Portfolio</h1>
<v-card
v-for="art in arts"
class="ma-5"
v-bind:key="art"
style="width: 15em; height: 15em; display:inline-flex"
>
<v-img :src="art" v-on:click="overlay=true"></v-img>
<v-overlay :absolute="absolute" :opacity="opacity" :value="overlay">
{{art}}
</v-overlay>
</v-card>
</v-container>
</div>
</template>
This is untested, but something like this should work... basically restructure your data a bit. Right now you have one overlay data boolean. Whenever you are setting this to true, you are setting it to true for everything that is using that same boolean... in your case all of your overlayables are looking at that same boolean.
Also I would recommend moving your styles into CSS classes, it will make this way more readable...
You need to update your HTML to this:
<template>
<div style="width: 100%; height: 100%">
<v-container fluid style="height:100%; background-color:#fc8276; width:25%; float:left"></v-container>
<v-container style=" height: 100%; width: 75%; float:right" fluid>
<h1>Portfolio</h1>
<v-card
v-for="(art, index) in arts"
class="ma-5"
v-bind:key="index"
style="width: 15em; height: 15em; display:inline-flex"
>
<v-img :src="art" v-on:click="showOverlay(art, index)"></v-img>
<v-overlay :absolute="absolute" :opacity="opacity" :value="art.overlay">
{{art.asset}}
</v-overlay>
</v-card>
</v-container>
</div>
</template>
Then change your data a bit:
data: () => ({
arts: [
{ asset: require("#/assets/art1.jpg"), overlay: false },
{ asset: require("#/assets/art2.jpg"), overlay: false },
{ asset: require("#/assets/art3.jpg"), overlay: false },
{ asset: require("#/assets/art4.jpeg"), overlay: false },
{ asset: require("#/assets/art5.jpg"), overlay: false },
{ asset: require("#/assets/art6.jpg"), overlay: false },
{ asset: require("#/assets/art7.jpg"), overlay: false },
{ asset: require("#/assets/art8.jpg"), overlay: false }
],
absolute: true,
opacity: 1,
})
Then in your methods:
methods: {
showOverlay(art, index) {
this.arts.map((a) => a.overlay = false);
this.arts[index].overlay = true;
}
}
I would make a new component which holds the v-card, so each one would have its own overlay data property. But having it all in one component with arts.length overlay variables works as well

How to place v-text-field error message in vuetifyjs?

I have v-text-field and I can't able to display the error message out of v-text-field dom position. in vuetify documentation there is not reference to this issue.
Is it possible to have the error message near the input?
Actual:
Expected:
<template>
<v-app>
<v-content>
<playground></playground>
<v-text-field
style="width:120px;"
class="numer"
:rules="[rules.required, rules.min, rules.max]"
v-model="numValue"
type="number"
append-outer-icon="add"
#click:append-outer="increment"
prepend-icon="remove"
#click:prepend="decrement"
></v-text-field>
{{numValue}}
</v-content>
</v-app>
</template>
<script>
import Playground from "./components/Playground";
export default {
name: "App",
components: {
Playground
},
data: function() {
return {
numValue: 0,
form: {
min: 2,
max: 10
},
rules: {
required: value => !!value || "Required.",
min: v => v >= this.form.min || `The Min is ${this.form.min}`,
max: v => v <= this.form.max || `The Max is ${this.form.max}`
}
};
},
methods: {
increment() {
if (this.numValue < this.form.max) {
this.numValue = parseInt(this.numValue, 10) + 1;
}
},
decrement() {
if (this.numValue > this.form.min) {
this.numValue = parseInt(this.numValue, 10) - 1;
}
}
}
};
</script>
<style>
.numer {
flex: 0 0 auto;
margin: 0;
padding: 0;
}
.numer input {
text-align: center;
}
</style>
The code in codesandbox
I know I'm late but if someone needs help:
Put this field inside a form.
<v-form ref="form">
<v-text-field
style="width:120px;"
class="numer"
:rules="[rules.required, rules.min, rules.max]"
v-model="numValue"
type="number"
append-outer-icon="add"
#click:append-outer="increment"
prepend-icon="remove"
#click:prepend="decrement"
></v-text-field>
</v-form>
Then call anywhere on template:
<div #click="$refs.form.validate()">Validate it!</div>
Or on the functions:
methods: {
foo() {
this.$refs.form.validate()
}
}

Vue.js: How to update element's position

I want to move an element while scrolling so the element is always in the screen.
But the position isn't updated.
What I am doing is this.
<v-card class="item" :style="{ top: distance }">
</v-card>
...
data() {
return {
distance: 0,
}
}
methods: {
handleScroll() {
this.distance = window.scrollY
},
},
created() {
window.addEventListener('scroll', this.handleScroll)
},
destroyed() {
window.removeEventListener('scroll', this.handleScroll)
},
}
...
.item {
position: absolute;
}
How can I do it?
Your element should have an absolute position and add the units as #Nikos mentioned :
<v-card class="item" style="position:absolute" :style="{ top: distance+'px' }">
</v-card>

How to get the value with konva at vue.js

I can move and transfer the rectangles with the code below.
I used konva library at vue.js
This works well.
But I want to get the x,y position to save into local-storage after moving it
Could you teach how to get that?
And I am sorry for the long code
You can attach this code at '.vue' which works well without problem.
It moves and transfer well, but I can 't see the value of position moving it
<template>
<div>
<v-stage ref="stage" :config="stageSize" #mousedown="handleStageMouseDown">
<v-layer ref="layer">
<v-rect v-for="item in rectangles" :key="item.id" :config="item"/>
<v-transformer ref="transformer"/>
</v-layer>
</v-stage>
<div>
<p>{{ rectangles[0].x }}</p>
<button #click="addCounter">+</button>
<button #click="subCounter">-</button>
<button #click="position">SAVE</button>
</div>
</div>
</template>
<script>
const width = window.innerWidth;
const height = window.innerHeight;
export default {
data() {
return {
stageSize: {
width: width,
height: height
},
rectangles: [
{
x: 150,
y: 100,
width: 100,
height: 100,
fill: "red",
name: "rect1",
draggable: true
},
{
x: 150,
y: 150,
width: 100,
height: 100,
fill: "green",
name: "rect2",
draggable: true
}
],
selectedShapeName: ""
};
},
methods: {
position() {
localStorage.setItem(this.rectangles[0].x, true);
},
addCounter() {
this.rectangles[0].x++;
},
subCounter() {
this.rectangles[0].x--;
},
handleStageMouseDown(e) {
// clicked on stage - cler selection
if (e.target === e.target.getStage()) {
this.selectedShapeName = "";
this.updateTransformer();
return;
}
// clicked on transformer - do nothing
const clickedOnTransformer =
e.target.getParent().className === "Transformer";
if (clickedOnTransformer) {
return;
}
// find clicked rect by its name
const name = e.target.name();
const rect = this.rectangles.find(r => r.name === name);
if (rect) {
this.selectedShapeName = name;
} else {
this.selectedShapeName = "";
}
this.updateTransformer();
},
updateTransformer() {
// here we need to manually attach or detach Transformer node
const transformerNode = this.$refs.transformer.getStage();
const stage = transformerNode.getStage();
const { selectedShapeName } = this;
const selectedNode = stage.findOne("." + selectedShapeName);
// do nothing if selected node is already attached
if (selectedNode === transformerNode.node()) {
return;
}
if (selectedNode) {
// attach to another node
transformerNode.attachTo(selectedNode);
} else {
// remove transformer
transformerNode.detach();
}
transformerNode.getLayer().batchDraw();
}
}
};
</script>
You can use dragmove and transform events.
<v-rect
v-for="item in rectangles"
:key="item.id"
:config="item"
#dragmove="handleRectChange"
#transform="handleRectChange"
/>
handleRectChange(e) {
console.log(e.target.x(), e.target.y()); // will log current position
},
Demo: https://codesandbox.io/s/lp53194w59