vue state transition with array data - javascript

I want to build a state transition based on the "Animating State with Watchers" in the vue.js docs: https://v2.vuejs.org/v2/guide/transitioning-state.html#Animating-State-with-Watchers
I understand the example with primitive datatypes. In my case the data numbers, which I need to animate, are values in objects stored in an array.
Here is a simplified example of my problem:
new Vue({
el: '#animated-bar-demo',
data: {
bars: [
{
width: 30,
},
{
width: 10,
},
{
width: 70,
},
]
},
// HOW TO WATCH AND ANIMATE BARS.WIDTH FOR EACH BAR?
/* computed: {
animatedNumber: function() {
return this.tweenedNumber.toFixed(0);
}
},
watch: {
number: function(newValue) {
TweenLite.to(this.$data, 0.5, { tweenedNumber: newValue });
}
}, */
methods: {
changeData() {
let bars = this.bars;
bars.forEach(bar => {
bar.width += 10;
});
this.bars = bars;
},
},
})
body {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
}
button {
margin-bottom: 24px;
}
.bar-container {
width: 400px;
height: 32px;
border: 3px solid black;
overflow: hidden;
margin-bottom: 32px;
}
.bar {
width: 120%;
height: 100%;
background-color: #FA7268;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>
<div id="animated-bar-demo">
<button v-on:click="changeData()"> add data </button>
<div class="bar-container" v-for="bar in bars">
<div class="bar"
:style="{ 'width': bar.width + '%'}">
</div>
</div>
</div>
I've commented the computed and watch logic for primitive values in my example out. How to watch and animate the width of each bar in the bars array?
I really appreciate your help and explanation for this problem.
Thanks!

I didn't found a solution to iterate an array through the tweenLite method but I got it to work.
I've created a component for the bar element and passing a single object width the width included.
Maybe someone with the same problem finds this solution helpful.

Related

why are chart.js line graphs overlapping each other?

I have seen similar questions on StackOverflow, but none of the answers have helped me so far.
I am using chart.js in my web project, but now, when I want to show 2 line graphs in the same container they do weird stuff, even if I adjust the parent container or try to wrap the canvas with a div.
here is some of the code:
[...]
<!--the big white container-->
<div class="Card Fill" id="5da78c0de214b0020e86f53a">
<div class="Details">
<span>entradas:30</span>
<span>duracion: 0:02</span>
</div>
<div class="ChartContainer">
<div class="ChartWraper">
<canvas id="tempGraph"></canvas>
</div>
<div class="ChartWraper">
<canvas id="ppmGraph"></canvas>
</div>
</div>
</div>
[...]
function drawChart(canvas, datasetLabel, data, labels) {
window[datasetLabel] = new Chart(
document.getElementById(canvas),
{
type: 'line',
data: {
datasets: [
{
lineTension: 0.1,
label: datasetLabel,
fill: true,
data: data
}
],
labels: labels
},
options: {
scales: {
xAxes: [{
display: true
}]
}
}
}
);
}
//ask api for data array
window.onload = function () {
let droneId = document.getElementsByClassName("Card")[0].id;
axios.get(`/api/vuelos/data/${droneId}`)
.then((response) => {
responseData = response.data;
let factorized = refractor(response.data); //separate the data from the drone into arrays
drawChart("tempGraph", "temperatura", factorized.temp, factorized.labels);
drawChart("ppmGraph", "particulas por millon", factorized.ppm, factorized.labels);
})
.catch((error) => {
alert(error.message);
console.log(error);
});
}
.Card {
position: relative; /* soo that i can relocate the options inside*/
background: var(--background);
padding: 30px;
border-radius: 15px;
margin: 10px;
box-shadow: var(--ui-shawdow);
}
.Fill{
width: 100%;
height: 100%;
}
.Card > .Details {
padding: 10px;
display: flex;
flex-direction: column;
align-content: space-around;
}
.ChartContainer{
display: flex;
flex-direction: column;
align-content: space-between;
height: 50%;
}
.ChartWraper{
height: 40%;
width: 100%;
}
canvas{
height: 40%;
}
What may I be doing wrong? I have tried changing things at the CSS or the Html but does not seem to work
I want it to look moething like this, responsive withouth overflow out of the card.

Position coordinates of an element related to viewport (not relative) via JS

I am building a vueJs web-app and I need the position of an element inside my web-app according to the viewport (not to the relative parent element). I wonder if there is a function/property doing this.
.offsetLeft is not what I need, because the element is inside of parent-elements with position: relative.
Please check out my pen: https://codepen.io/mister-hansen/pen/aGdWMp
(With an example what different position: relative makes.)
HTML
<div id="app">
<div class="box">
<div class="box__in" ref="my_box_a">
What is my position?
<br> offsetLeft: <strong>{{posBoxA}}</strong>
</div>
</div>
<div class="box box--relative">
<div class="box__in" ref="my_box_b">
What is my position in relative box?
<br>
offsetLeft: <strong>{{posBoxB}}?!</strong>
</div>
</div>
</div>
JS - VueJs
const app = new Vue({
el: '#app',
data () {
return {
posBoxA: 0,
posBoxB: 0,
}
},
mounted () {
this.calcPosOfBox()
},
methods: {
calcPosOfBox () {
this.posBoxA = this.$refs['my_box_a'].offsetLeft
this.posBoxB = this.$refs['my_box_b'].offsetLeft
}
}
})
SCSS
html, body {
margin: 0;
}
#app {
padding: 10vh 100px;
font-family: sans-serif;
}
.box {
margin: 0 auto 10vh;
padding: 10vh 50px;
background: lightgrey;
&--relative {
border: 1px solid red;
position: relative;
}
&__in {
padding: 1rem;
background: lightgreen;
}
}
Use getBoundingClientRect(). The x and y returns are relative to the top-level viewport.
Emphasis mine:
The returned value is a DOMRect object which is the union of the
rectangles returned by getClientRects() for the element, i.e., the CSS
border-boxes associated with the element. The result is the smallest
rectangle which contains the entire element, with read-only left, top,
right, bottom, x, y, width, and height properties describing the
overall border-box in pixels. Properties other than width and height
are relative to the top-left of the viewport.
const app = new Vue({
el: '#app',
data() {
return {
posBoxA: 0,
posBoxB: 0,
}
},
mounted() {
this.calcPosOfBox()
},
methods: {
calcPosOfBox() {
const boxABB = this.$refs["my_box_a"].getBoundingClientRect();
const boxBBB = this.$refs["my_box_b"].getBoundingClientRect();
this.posBoxA = boxABB.x;
this.posBoxB = boxBBB.x;
}
}
})
html,
body {
margin: 0;
}
#app {
padding: 10vh 100px;
font-family: sans-serif;
}
.box {
margin: 0 auto 10vh;
padding: 10vh 50px;
background: lightgrey;
}
.box--relative {
border: 1px solid red;
position: relative;
}
.box__in {
padding: 1rem;
background: lightgreen;
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.min.js"></script>
<div id="app">
<div class="box">
<div class="box__in" ref="my_box_a">
What is my position?
<br> offsetLeft: <strong>{{posBoxA}}</strong>
</div>
</div>
<div class="box box--relative">
<div class="box__in" ref="my_box_b">
What is my position in relative box?
<br> offsetLeft: <strong>{{posBoxB}}?!</strong>
</div>
</div>
</div>

Changing cursor for draggable not working in chrome using vue.js

I am trying to change the cursor of the draggable item in chrome. Everything i tried it is not working. There are solution on Stackoverflow but they are all outdated and not working with the actual chrome version.
On drag the item is copied to a container which is the dragimage for the draggable.
What i want is to have a grabbing cursor while dragging. How would that be possible? Any Ideas?
See my code snippet for an example.
new Vue({
el: '#app',
data: {
text_drop: 'Droppable Area',
text_drag: 'Drag Area',
drag_elements: [
{text: 'one', selected: true},
{text: 'two', selected: false},
{text: 'three', selected: false},
{text: 'four', selected: false},
]
},
computed: {
selected_elements(){
let selected = [];
this.drag_elements.map((drag) => {
if(drag.selected){
selected.push(drag);
}
})
return selected;
}
},
methods: {
drag_it(event){
let html = document.getElementById("dragElement");
let drop_docs = this.selected_elements;
if(drop_docs.length > 1){
let multiple = document.createElement('div');
multiple.classList.add('dragMultiple');
multiple.innerHTML = drop_docs.length + ' items';
html.innerHTML = '';
html.appendChild(multiple)
}else{
html.innerHTML = event.target.outerHTML;
}
event.dataTransfer.setData('text/plain', '' );
event.dataTransfer.setDragImage(html, 0, 0);
event.dataTransfer.effectAllowed = "move";
},
drag_over(event){
document.documentElement.style.cursor="-webkit-grabbing";
},
drag_end(event){
document.documentElement.style.cursor="default";
},
select(event, drag_element){
if(event.metaKey || event.shiftKey){
drag_element.selected = !drag_element.selected;
} else {
this.drag_elements.map((drag) => {
if(drag === drag_element){
drag.selected = true;
}else{
drag.selected = false;
}
})
}
}
}
})
#Dragme{
width: 200px;
height: 50px;
margin-left: 20px;
text-align: center;
border:1px solid black;
float:left;
}
#Dragme:hover {
cursor: -webkit-grab;
}
#Dragme:active {
cursor: -webkit-grabbing;
}
#Dropzone{
float: left;
width: 500px;
height: 100px;
border: 1px solid;
margin-bottom: 50px;
}
.selected{
border: 2px solid yellow !important;
}
.dragMultiple{
border: 1px solid black;
padding: 10px;
background-color: white;
}
#dragElement{
position: absolute;
top: 400px;
}
<script src="https://vuejs.org/js/vue.min.js"></script>
<div id="app">
<div id="Dropzone">{{text_drop}}</div>
<div id="drag_elements">
<div v-for="drag in drag_elements"
#dragstart="drag_it"
#dragover="drag_over"
#dragend="drag_end"
#mouseup="select($event, drag)"
draggable="true"
:class="{selected: drag.selected}"
id="Dragme">{{drag.text}}</div>
</div>
</div>
<div id="dragElement">
</div>
Update
Actually it can be solved with the following answer
CSS for grabbing cursors (drag & drop)
It is important to add the dndclass
thx
Blockquote
#Carr for the hint
Update
After Dragend or drop the cursor is not set to default. Only when moved it changes back. Any Ideas?
Update
With they command key on mac or the shift key multiple items can be selected and dragged. A new dragitem is created for that purpose but the cursor does not allways fall back after dragend or drop.
Update
Integrate method to from answer -by Carr
In fact, setDragImage api is to set the image for replacing that plain document icon which be aside with default cursor, not cursor itself. So your code about '.dragElement' is not working as you expected, it's unstable and causes weird effect when I am testing, I have removed them in my answer.
What I've done below is a little bit tricky, but I think it's at least in correct logic. However, maybe there is a more elegant solution.
new Vue({
el: '#app',
data: {
text_drop: 'Droppable Area',
text_drag: 'Drag Area'
},
methods: {
drag_it(event){
event.dataTransfer.setData('text/plain', '' );
event.dataTransfer.effectAllowed = "move";
},
drag_over(event){
document.documentElement.style.cursor="-webkit-grabbing";
},
drag_end(event){
document.documentElement.style.cursor="default";
}
}
})
#Dragme{
width: 200px;
height: 50px;
text-align: center;
border:1px solid black;
float:left;
}
#Dragme:hover {
cursor: -webkit-grab;
}
#Dragme:active {
cursor: -webkit-grabbing;
}
#Dropzone{
float: left;
width: 300px;
height: 100px;
border: 1px solid;
margin-bottom: 50px;
}
<script src="https://vuejs.org/js/vue.min.js"></script>
<div id="app">
<div id="Dropzone">{{text_drop}}</div>
<div #dragstart="drag_it"
#dragover="drag_over"
#dragend="drag_end"
draggable="true"
id="Dragme">{{text_drag}}</div>
</div>
Update - derivative problems about original question
"dragImage" sticks at bottom, all elements are disappeared, or flashing sometimes.
And here is still a weird part, id attribute should be unique:
And add quote from MDN document about setDragImage, I wrongly recalled svg in comment, it should be canvas :
... The image will typically be an <image> element but it can also be a
<canvas> or any other image element. ...
We could draw text in canvas, it's another question.

Splitting boxes to 50% height of their parent

I would like to split the active items to 50% height of their parent element. So when I open the first two items, they should split to 50% of their parent class .items (both .item have 100px). So I can see both without scrolling. Also when I open all three of them, they should get the height of 100px, which is the half of their parent. My problem is, tha the second item overlaps and I have to scroll. Whats wrong?
angular.module("myApp", []).controller("myController", function($scope) {
$scope.itemList = [{
id: 1,
name: "Item 1",
isOpen: false
}, {
id: 2,
name: "Item 2",
isOpen: false
}, {
id: 3,
name: "Item 3",
isOpen: false
}];
$scope.setHeight = function() {
if ($scope.itemList.length > 1) {
var typeHeaderHeight = $('.item-header').outerHeight();
var halfHeight = Math.round($('.items').outerHeight() / 2);
setTimeout(() => {
$('.item').css('height', typeHeaderHeight);
$('.item.active').css('height', halfHeight);
});
}
}
});
.frame {
display: flex;
flex-direction: column;
height: 200px;
}
.items {
display: flex;
flex: 1 1 auto;
flex-direction: column;
overflow: auto;
border: 1px solid black;
}
.item {
display: flex;
flex-direction: column;
flex: 0 0 auto;
margin-bottom: 5px;
background-color: rgb(150, 150, 150);
color: white;
}
.item:hover {
cursor: pointer;
}
.item-header {
position: relative;
display: flex;
}
.active {
background-color: rgb(220, 220, 220);
color: rgb(150, 150, 150);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="frame" ng-app="myApp" ng-controller="myController">
<div class="items">
<div class="item" ng-repeat="item in itemList" ng-click="item.isOpen = !item.isOpen; setHeight()" ng-class="{active: item.isOpen}">
<div class="item-header">
{{item.name}}
</div>
</div>
</div>
</div>
The issue is that you are setting the values of typeHeaderHeight and halfHeight variables outside the timeout so it always calculated before the item is actually opened so there is mistake in the calculations
try to make it like
setTimeout(() => {
var typeHeaderHeight = $('.item-header').outerHeight();
var halfHeight = Math.round($('.items').outerHeight() / 2);
$('.item').css('height', typeHeaderHeight);
$('.item.active').css('height', halfHeight);
});
},500)
One more thing .. I don't recommend to use setTimeout of vanilla JavaScript try to use $timeout instead of it

why I can't set the same height and width by using jQuery?

I wish to build a sketchpad by using jQuery to dynamically set width and height for each div grid.
However the result shows a different size of height and width. From the JQuery code, I understand that I have successfully created 16*16 div blocks. I then assign it with height and width by using selector.css(width:function(){}).
As a result, I am expecting to see a 16 * 16 grid of blocks with the same size. However, it displays different sizes of blocks. I have no clue why this happens, can anyone enlighten me?
var input = 16;
$(document).ready(function(){
for(var i = 0; i<input*input;i++){
$('.wrapper').append("<div></div>");
}
$('.wrapper').find('div').css({
width: function(input) {
return 200/input;
},
height: function(input) {
return 200/input;
}
});
$('.wrapper').find('div').addClass('grid');
$('.wrapper').find('div').on('mouseenter',function(){
$(this).addClass('highlight');
});
});
.wrapper{
width: 900px;
height: 900px;
margin: 0 auto;
}
.grid{
border: 1px solid black;
display: inline-block;
margin: 2px 2px;
}
.highlight{
background-color: blue;
}
#button{
width: 900px;
margin: 0 auto;
}
button{
display: block;
margin: auto;
}
body{
background-color: grey;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<script type="text/javascript" src = "sketchpad.js"></script>
<div class="wrapper"></div>
$('.wrapper').find('div').width(200/input);
$('.wrapper').find('div').height(200/input);
/*$('.wrapper').find('div').css({
width: function(input) {
return 200/input;
},
height: function(input) {
return 200/input;
}
});*/
reomve commented code section and add above code section.
$('.wrapper').find('div').css({
width: function(input) {
return 200/input;
},
height: function(input) {
return 200/input;
}
});
I think you just forgot to put width and height unit.
$('.wrapper').find('div').css({
width: function(input) {
return 200/input + 'px';
},
height: function(input) {
return 200/input + 'px';
}
});
Should work
The problem is with the width function you specified:
width: function(input) { return 200/input; },
According to the documentation this function:
Receives the index position of the element in the set and the old
width as arguments.
Thus the parameter input contains the index position of div, e.g. 0, 1, 2, etc. That is why you see blocks which are invisible (position 0), have a height/width of 200 (position 1), etc.
The solution is to use a variable instead of a function to set the width:
var block_width = 200 / input;
$('.wrapper').find('div').css({
width: block_width,
height: block_width
});

Categories