Set animation direction via Javascript/Typescript - javascript

Through Typescript I have to set an animation in a component defined in the html template.
To do this I am using the "animate (keyframes, options)" method.
In the options I would like to set the "alternate" direction but I don't know how to do it because the property is of type Playbackdirection which is an interface. How can I do it?
Here my code:
animate() {
const progress = document.querySelector(".progress") as HTMLElement;
if (progress !== null){
const options = {
duration: 2000,
easing: "ease-in-out",
iterations: 2,
direction: "alternate" //ERROR HERE
}
progress.animate({
width: ["0%", "10%", "20%", "30%", "40%", "50%", "60%", "70%", "80%", "80%", "80%"],
backgroundSize: ["0%", "1000%", "500%", "333%", "250%", "200%", "166%", "142%", "125%", "125%", "125%"]
}, options);
}
}

direction is an enum, so you should set it to "normal" or "reverse".
const options = {
...
direction: "reverse"
}

Related

React Native Animated setValue() problem?

Actually I'm trying to set value of the animation with setValue() after Animated.timing() is finished and want to use this updated animated value then in a loop animation.
//Initialising animation value=50
const leftAnim = useRef(new Animated.Value(50)).current
useEffect(() => {
Animated.timing(leftAnim,{
toValue:360,
duration:3000,
easing:Easing.linear,
useNativeDriver:false,
}).start(({finished}) => {
//Updating animation value=100
leftAnim.setValue(100)
//Animated API is not considering the setValue and starting the loop animation with the first value i.e 50 instead of 100
Animated.loop(
Animated.timing(leftAnim,{
toValue:360,
duration:5000,
easing:Easing.linear,
useNativeDriver:false
})
).start()
})
},[])
Am I doing something wrong? Is there a better way to do it?
You can use leftAnim.setOffset(nextStart) and inside loop adjust end accordingly.
Demo on snack expo
import React, { Component, useRef, useEffect, useState } from 'react';
import { Easing, StyleSheet, View, Animated, Button, Text } from 'react-native';
const start = 0;
const end = 100;
export default Anim = () => {
const leftAnim = useRef(new Animated.Value(start)).current;
const [curValue, setCurValue] = useState(start);
useEffect(() => {
leftAnim.addListener((v) => {
setCurValue(v.value.toFixed(0));
});
Animated.timing(leftAnim, {
toValue: end,
duration: 5000,
easing: Easing.linear,
useNativeDriver: false,
}).start(({ finished }) => {
//setting value to 80
leftAnim.setOffset(80);
// increment only by 20, 80 + 20 = 100
Animated.loop(
Animated.timing(leftAnim, {
toValue: end - 80,
duration: 2000,
easing: Easing.linear,
useNativeDriver: false,
})
).start();
});
}, [leftAnim]);
return (
<View style={styles.container}>
<Animated.Image
source={require('./assets/snack-icon.png')}
style={{ width: 40, height: 40, transform: [{ translateY: leftAnim }] }}
/>
<Text>Current Value: {curValue}</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'start',
alignItems: 'center',
padding: 10,
paddingTop: 50,
},
input: {
height: 50,
marginHorizontal: 15,
backgroundColor: '#ededed',
marginTop: 10,
paddingHorizontal: 9,
},
});
Repalce this:
leftAnim.setValue(100)
With this:
leftAnim._startingValue = 100
Now, when the loop animation starts, it will start from 100 because we've changed the starting value.
Snack Link

react-particles-js: Can't set a background and width

I have this installed react-particles-js, everything works fine as I see, but now I have created some custom design, and assigned it to the Particle via it params, just like this:
<Particles
params={{
particles: {
number: { value: 35, density: { enable: true, value_area: 800 } },
color: {
value: [
'BB4D10',
'#820E42',
'#BD740F',
'#248592',
'#5F4DAF',
'#8BA00F',
],
},
shape: {
type: 'circle',
stroke: { width: 0, color: '#000000' },
polygon: { nb_sides: 3 },
image: { src: 'img/github.svg', width: 100, height: 100 },
},
opacity: {
value: 1.5,
random: false,
anim: {
enable: false,
speed: 1,
opacity_min: 0.1,
sync: false,
},
},
size: {
value: 9,
random: true,
anim: {
enable: false,
speed: 43.15684315684316,
size_min: 0.1,
sync: false,
},
},
line_linked: {
enable: true,
distance: 100.82952832645452,
color: '#ffffff',
opacity: 1.4,
width: 1,
},
move: {
enable: true,
speed: 2,
direction: 'none',
random: true,
straight: false,
out_mode: 'out',
bounce: false,
attract: { enable: false, rotateX: 600, rotateY: 1200 },
},
},
interactivity: {
detect_on: 'canvas',
events: {
onhover: { enable: true, mode: 'bubble' },
onclick: { enable: false, mode: 'push' },
resize: true,
},
modes: {
grab: { distance: 400, line_linked: { opacity: 1 } },
bubble: {
distance: 109.63042366068159,
size: 13,
duration: 2,
opacity: 8,
speed: 3,
},
repulse: { distance: 200, duration: 0.4 },
push: { particles_nb: 4 },
remove: { particles_nb: 2 },
},
},
retina_detect: true,
}}
style={styling}
/>
After that, I'm trying to add a background color to make it look correctly, but as I can see the styling which I try to pass to the particle it just doesn't work, at least the position applies.
const styling = {
backgroundColor: '#2a2e31',
position: 'absolute',
width: '10%',
};
There are several ways to control the styling:
You can provide a className prop for canvas wrapper, and width prop for the width of canvas:
<Particles
...
style={styling}
width="10%"
className="particles-wrapper"
/>
And, write the CSS:
.particles-wrapper {
background-color: #2a2e31;
height: 100%;
width: 10%; // You may need this or may not depending on your requirement
}
Or, you can add a new div which would wrap the <Particles .. /> and set styling for this div as by default canvas is transparent.
Or, you can use canvasClassName prop and set the style for this class.
Here is a snapshot after using the 1st method from above:
The reason that we need to pass width, height separately is, as seen in source code, those present in props.style is being overwritten like this:
style={{
...this.props.style,
width,
height
}}
The easiest way to set a background to the particles is using the background option.
<Particles params={{
background: {
color: "#000"
},
/* all other options you set */
}} />
This will set a background to the canvas. If you need a transparent one use #ajeet-shah answer.

Deleting all zero-opacity elements in Fabricjs

I'm using the fabricjs animate function to change the opacity of text elements on a canvas.
For every frame, I need to check for elements with 0% opacity and remove them with canvas.remove.
At the moment, I've come up with this code which I'm running for each fire of requestAnimationFrame:
canvas.getObjects().filter((obj) => obj.get("opacity") === 0).forEach(canvas.remove)
However, when iterating, filtering through the items and running canvas.remove, I'm getting Uncaught TypeError: Cannot read property 'indexOf' of undefined.
Here's a simple implementation of this problem (not the actual code):
const canvas = new fabric.StaticCanvas(document.querySelector("canvas"), { backgroundColor: "black" })
// CODE HERE:
function removalLogic() {
canvas.getObjects().filter((obj) => obj.get("opacity") === 0).forEach(canvas.remove)
}
const rect = new fabric.Rect({
width: 100, height: 100,
left: 10, top: 20,
fill: "grey",
})
canvas.add(rect)
rect.animate("opacity", "0", {
duration: 2500,
onChange: canvas.renderAll.bind(canvas),
onComplete: removalLogic,
})
<canvas height="512" width="512"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.5.0/fabric.min.js"></script>
Use .map() to iterate over all the objects of canvas.
Use canvas.remove(obj) to remove the object.
This statement of yours was incorrect obj.get("opacity") === 0).forEach
const canvas = new fabric.StaticCanvas(document.querySelector("canvas"), { backgroundColor: "black" })
// CODE HERE:
function removalLogic() {
console.log(canvas.getObjects().length);
canvas.getObjects().map((obj) => ((obj.get("opacity") == 0)? canvas.remove(obj) :''))
console.log(canvas.getObjects().length);
}
const rect = new fabric.Rect({
width: 100, height: 100,
left: 10, top: 20,
fill: "grey",
})
canvas.add(rect)
rect.animate("opacity", "0", {
duration: 2500,
onChange: canvas.renderAll.bind(canvas),
onComplete: removalLogic,
})
<canvas height="512" width="512"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.5.0/fabric.min.js"></script>

Google chart throws error for invalid color due to values received using DataMap

Google chart throws error for invalid color due to values received using DataMap.
You can see colors: [colors], in options.
If i direct use color values like below then it works fine.
colors: ['#006400', '#3cb371', 'red', '#f5fffa'],
but if i get values through data map and it has same output then it doesn't work and throws error:
"'#006400' is not a valid color string".
Is there any kind of data formatting issue?
Sample JSON Data:
{"cols":[{"label":"status","type":"string"},{"label":"count","type":"string"}],"rows":[{"c":[{"v":"CLOSED"},{"v":3}]},{"c":[{"v":"VERIFIED"},{"v":35}]},{"c":[{"v":"RESOLVED"},{"v":15}]},{"c":[{"v":"IN_PROGRESS"},{"v":92}]},{"c":[{"v":"ASSIGNED"},{"v":63}]},{"c":[{"v":"NEW"},{"v":16}]},{"c":[{"v":""},{"v":0}]}]}
Custom color (problematic):
const DataMap = {
CLOSED: '#006400',
VERIFIED: '#006400',
RESOLVED: '#3cb371',
REOPENED: 'red',
IN_PROGRESS: '#f5fffa',
ASSIGNED: 'brown',
NEW: 'brown',
UNCONFIRMED: 'brown'
};
let myColors = [];
Object.keys(DataMap).forEach((key, index) => {
if (jsonData.search(key) !== -1) {
myColors.push("'" + DataMap[key] + "'");
}
});
options = {
title: chartTitle,
width: '410',
height: '320',
backgroundColor: '#f5fffa',
is3D: true,
colors: [myColors],
chartArea: {
left: "20%",
top: "20%",
height: "100%",
width: "100%"
}
};
chart.draw(data, options);
Direct color (works fine):
options = {
title: chartTitle,
width: '410',
height: '320',
backgroundColor: '#f5fffa',
is3D: true,
colors: ['#006400', '#3cb371', 'red', '#f5fffa'],
chartArea: {
left: "20%",
top: "20%",
height: "100%",
width: "100%"
}
};
chart.draw(data, options);
What can be the issue and how can i resolve it?
What I can say right now is:
myColors.push("'" + DataMap[key] + "'");
should be changed to
myColors.push(DataMap[key]);
because values for each key from DataMap are already strings, i.e. DataMap[key] is a string.
Secondly, instead of
colors: [myColors],
should be just
colors: myColors,
because myColors is already an array.

Creating a set of views takes a long time (Appcelerator) and blocks user interactions on iOS only

I am creating a list (ScrollView) with some custom "rows" (The image contains a single "row"). I show 5 rows and have added an event listener so that when the user scrolls to the end, 5 more elements are loaded and displayed. I made this using Alloy but I noticed it was taking too long so I tried writing the views manually.
// created the views programmatically to see if there was any difference from Alloy
function createRow(args) {
var container = Ti.UI.createView({
layout: "vertical",
width: Ti.UI.FILL,
height: "42dp"
});
var rowContent = Ti.UI.createView({
width: Titanium.UI.FILL,
height: "41dp", //Titanium.UI.FILL,
layout: "horizontal",
left: "16dp",
right: "16dp"
});
var border = Ti.UI.createView({
left: "16dp",
right: "16dp",
height: "1dp",
backgroundColor: Colors.darkGrey
});
var titleScroll = Ti.UI.createScrollView({
scrollType: "horizontal",
width: "49%",
horizontalWrap: false
});
var scrollContainer = Ti.UI.createScrollView({
scrollType: "horizontal",
horizontalWrap: false,
width: "50%"
});
var scroll = Ti.UI.createView({
layout: "horizontal",
horizontalWrap: false,
right: 0,
width: Ti.UI.SIZE,
height: Titanium.UI.SIZE
});
var title = Ti.UI.createLabel({
text: args.title,
font: args.isTitle ? {font: "Lato-Regular", fontSize: "22dp"} : {fontFamily: "Lato-Regular", fontSize: "15"},
horizontalWrap: false,
wordWrap: false,
left: 0,
color: Colors.grey,
minimumFontSize: "15dp"
});
if(args.value) {
var t = args.value.join();
scroll.add(Ti.UI.createLabel({
text: t,
color: args.action ? Colors.blue : Colors.black,
font: {fontSize: "15dp", fontFamily: "Lato-Regular"},
right: "5dp",
width: Ti.UI.SIZE,
horizontalWrap: false,
wordWrap: false,
minimumFontSize: "15dp"
}));
}
if(args.data)
scrollContainer.data = args.data; //just a dump of the data used by the click handler
if(args.action)
scrollContainer.addEventListener("click",args.action);
scrollContainer.add(scroll);
titleScroll.add(title);
rowContent.add(titleScroll);
rowContent.add(scrollContainer);
container.add(rowContent);
container.add(border);
return container; //Ti.UI.View
}
function createHeader(args) {
var header = Ti.UI.createView({
layout: "horizontal",
height: "44dp",
backgroundColor: "#fff"
});
var leftView = Ti.UI.createView({
width: "25%",
height: Ti.UI.FILL
});
var rightView = Ti.UI.createView({
width: "25%",
height: Ti.UI.FILL
});
var centerView = Ti.UI.createView({
width: "49%",
height: Ti.UI.FILL
});
var verticalAligner = Ti.UI.createView({
height: Ti.UI.SIZE,
width: Ti.UI.SIZE,
layout: "vertical"
});
var headerTitle = Ti.UI.createLabel({
color: Colors.green,
font: {fontSize: "16.5dp", fontFamily: "Lato-Regular"},
textAlign: "center",
horizontalWrap: false,
wordWrap: false
});
var headerSubtitle = Ti.UI.createLabel({
font: {fontSize: "14dp", fontFamily: "Lato-Regular"},
textAlign: "center",
color: Colors.grey,
horizontalWrap: false,
wordWrap: false
});
if(args.rightView)
rightView.add(args.rightView);
if(args.leftView)
leftView.add(args.leftView);
verticalAligner.add(headerTitle);
verticalAligner.add(headerSubtitle);
centerView.add(verticalAligner);
header.add(leftView);
header.add(centerView);
header.add(rightView);
headerTitle.text = args.title;
headerSubtitle.text = args.subTitle;
return header;
}
function createBlock(args) {
var container = Ti.UI.createView({
layout: "vertical",
width: "100%",
height: Ti.UI.SIZE
});
var covers = Ti.UI.createView({ //
height: "119dp"
});
var content = Ti.UI.createView({
height: Ti.UI.SIZE,
layout: "vertical"
});
function goToEvent() {
Storage.event.id = args.event;
Alloy.Globals.openWin("event");
}
var data = new D.data();
var w = Android ? Ti.Platform.displayCaps.platformWidth : Measure.dpToPX(Ti.Platform.displayCaps.platformWidth);
var h = Measure.dpToPX(119); //Android ? Alloy.Globals.dpToPX(119) : Measure.dpToPX(119);
if(args.images) {
//setTimeout(function() { //timeout didn't make any difference
var image = null;
//for(var i = 0; i < args.images.length; i++) {
image = Ti.UI.createImageView({
image: data.getBlobResized({ //returns a URL for the picture
id: args.images[0], //i
width: w,
height: h
}),
width: iOS ? Measure.pxToDP(w) : Alloy.Globals.pxToDP(w),
height: iOS ? Measure.pxToDP(h): Alloy.Globals.pxToDP(h)
});
image.addEventListener("click",goToEvent);
covers.add(image); //addView
//}
//},0);
}
var row = null;
if(args.rows) {
for(var j=0; j < args.rows.length; j++) {
//row = Alloy.createController("index/events/block/row",args.rows[j]).getView();
content.add(createRow(args.rows[j]));
}
}
container.add(createHeader(args));
container.add(covers);
container.add(content);
return container;
}
In particular, in the code provided, I call 4 times the function createRow() which creates a row inside the element (as seen in the picture). This function takes 7ms to 10ms to run for some reason. So calling it 4 times means it slows the whole process 28-40ms.
On Android the app doesn't lag at all. On iOS it stops user interaction completely until these operations are done
Using latest Titanium SDK (5.2.2GA) on Appcelerator Studio
Testing on iPhone 5, iOS simulator (4s,5,6,6s)
Thank you for your help

Categories

Resources