function-like interpolation in React Native? - animation

I have a scrollview with fixed length in my RN project that should act like a parralax scroll behavior. When I scroll and move the Y component, the X component of the header is moving right so when it is on top, it is 56 pixels away from the left edge, leaving enough place for the back arrow.
But it is linear. Is there a way to make it exponential. The best example would be the WhatsApp contact's parralax scroll:
Watch the Title "Dune"
How I have it now = red line (linear)
How I would like to = blue line (linear with easing, exponential, whatever it's called)
I got the scaling animation done, but the linear motion is like a thorn in my eye and the documentation for Animated values is overwhelming and unclear a bit.
I've defined:
scrollY: new Animated.Value(0)
in state and in my scrollview like this:
<ScrollView
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {y: this.state.scrollY}}}]
)}
and my Animated.View inside of it looks like this:
<Animated.View
style={[
{marginTop: 30, alignSelf: 'flex-start' },
{translateX: headerTranslateX}
]}]}>
<Text>Title</Text>
</Animated.View>
Aand the interpolation:
const titleTranslateX = this.state.scrollY.interpolate({
inputRange: [0, HEADER_SCROLL_DISTANCE*0.6, HEADER_SCROLL_DISTANCE],
outputRange: [0, 0, 56],
extrapolate: 'clamp',
})
which is linear in nature (i tried setting 10+keypoints in inputRange and outputRange bit but it gets messy and doesn't look natural enough)
Any advice on how to achieve the desired effect?

The only thing it says in the Animated docs on easing (function interpolation) is:
Interpolation
Each property can be run through an interpolation first. An interpolation maps input ranges to output ranges, typically using a linear interpolation but also supports easing functions. By default, it will extrapolate the curve beyond the ranges given, but you can also have it clamp the output value.
It doesn't mention how you can add an easing function. However, in the source code you'll find this: https://github.com/facebook/react-native/blob/e2ce98b7c6f4f2fc7011c214f9edc1301ff30572/Libraries/Animated/src/Interpolation.js#L27
export type InterpolationConfigType = {
inputRange: Array<number>,
/* $FlowFixMe(>=0.38.0 site=react_native_fb,react_native_oss) - Flow error
* detected during the deployment of v0.38.0. To see the error, remove this
* comment and run flow
*/
outputRange: (Array<number> | Array<string>),
easing?: ((input: number) => number),
extrapolate?: ExtrapolateType,
extrapolateLeft?: ExtrapolateType,
extrapolateRight?: ExtrapolateType,
};
The easing function defaults to linear (t) => t, but you can make it any standard easing function. Here's a nice list: https://gist.github.com/gre/1650294
Note: this won't work if you're using useNativeDriver: true.
Hope this helps reduce the choppiness!

Related

Limit maximum zoom for a scene in Three.js

I have a Three.js App and I wanna limit the zoom for the scene, because logically at some zoom the user can get inside of my 3D object, which in my oppinion is not a really good UX.
I tried scene.maxZoom = number; but did not work. What can I do?
Here is the code: https://github.com/AlinAlexandruPeter/code/blob/main/code.js
You don't need Three.js to limit the range of numbers, just simple JavaScript. Use Math.min(a, b) to get the lower of the two values.
const MAX_ZOOM = 2.5;
// Clamp user input to 2.5 and below
camera.zoom = Math.min(userInput, MAX_ZOOM);
With this approach, if userInput = 3, it will get clamped at 2.5. There's also Math.max() if you want to clamp it on the lower range.

gsap Flip.fit svg element not moving to expected coordinates because of { duration: x }

I am confused with gsap's Flip.fit moving to coordinates.
I have a game board with 182 tiles and 182 playing tiles.
The goal
When the user clicks the bag, a random playing tile is selected and is "supposed" to move over the tile on the board.
If you change
Flip.fit(PTILE[tileArray], TILE[tileArray], {duration: 1 , scale: true});
when changing { duration: 0, ... } the move works as expected, however no animation. When duration is above zero, the final location is very random.
codepen
I'm not sure how the duration affects the final position, however, I found a way to get the positions right. That is reset the transform of your PTILE before telling GSAP to do the Flip animation.
// reset transform value
gsap.set(PTILE[tileArray], { transform: "" });
// animate with new transform value
Flip.fit(PTILE[tileArray], TILE[tileArray], {
duration: 1,
scale: true
});
My reason is that PTITLE and TITLE are placed in different <g> tags which means their transform systems are inconsistent. Plus, Flip.fit() will act like gsap.to() with new TITLE position is the to object, GSAP will try to calculate the from object from your original transforms which are already set in the SVG as transform:matrix(). This process, somehow, is messing up. So what I did is give GSAP an exact transform value for the from object, which is empty.
Ok, I found out that Inkscape stores the SVG with inline transforms that threw the animation off. I tried saving in plain or optimised, but still had no luck.
So there are two solutions.
Use SVGOMG an online SVG cleaner.
Use Affinity Designer application which can export and flatten transforms.
The key to rule out other factors is to use relative coordinates and flatten transforms.
I have included a screenshot of Affinity exporting options.
Affinity Export screenshot

drawing lots of identical features — what's the best approach?

I need to draw a large number of rectangles (potentially up to millions), which are located all over the world. I was wondering what the optimal approach is to achieve the best possible performance. my requirements are:
all items are rectangles (not squares), and identical in size and color
their rotation is different per individual item though
they have different fixed locations – they do not move
the rectangles need to be pickable
they might need to be scaled according to current zoom level (to make them look like real objects on the ground)
use the webgl renderer
what I have tried to far:
const features = R.map(
(i) => {
// [...] calculate `coords` and `rotation`
const point = new ol.geom.Point(coords);
const feature = new ol.Feature(point);
feature.__angle = rotation;
return feature;
},
R.range(0, count /* lots of them! */)
);
const sheetStyle = new ol.style.Style({
image: new ol.style.Icon({
size: [5, 8], // shape of rectangle
src: 'color.png' // 1×1px image
})
});
const vectorLayer = new ol.layer.Vector({
source: new ol.source.Vector({ features }),
preload: Infinity,
updateWhileAnimating: true,
updateWhileInteracting: true,
style: (feature, resolution) => {
const image = sheetStyle.getImage();
// TODO: is there a way to only have to do this once?
image.setRotation(feature.__angle);
// scale according to zoom level
image.setScale(0.3 / resolution);
return sheetStyle;
},
});
I was wondering if ol3 was doing any sort of optimization under the hood.
does it merge the geometries into one?
does it only display items that in the visible part of the map?
since all items are identical, is there a way to use instancing?
related: for better performance, I am only creating a single style object that I am reusing for all items. however I need to set a rotation on each of them, which is why I am using a style function. once it is set, the rotation won't change anymore though. is there a way around having to call the style function every frame?
I am also considering using a heatmap layer for lower zoom levels and then switching to the vector layer as the user zooms in.
it would be great if someone could give me hints for overall performance improvements.

Snap SVG animating existing matrix

I use Snap.svg to create a simple card game. I loaded drawed cards from file and moved them to specific location using matrix translate.
It's svg element now looks kinda like this:
<g id="card11" inkscape:label="#g3908" transform="matrix(1.5621,0,0,1.5621,625.1085,529.3716)" cardposition="4" style="visibility: visible;" class="card inhand hand-4 ofplayer1">...</g>
However, now I'm trying to animate them to a specific position (same for all cards) using this:
function animateTo(object, x, y, scaleX, scaleY, time) {
var matrix = object.transform().localMatrix;
var added = new Snap.Matrix();
added.translate(x, y);
added.scale(scaleX, scaleY);
added.add(matrix);
object.animate({transform: added}, time);
}
or something like this:
function animateTo(object, x, y, scaleX, scaleY, time) {
object.animate({transform: "t100,100"}, time);//this one I tried to use to understand how snap animations works
}
And here is my problem - when it animates, it allways first deletes the animation matrix of object and start animate from it's original location with blank matrix (where the object would be without transform attribute).
For example, when I tried:
var matrix = object.transform().localMatrix;
object.animate({transform: matrix}, time);
I expected it will do nothing, but my object blinks to the top left corner (blank matrix) and animates to position where it should stay.
What am I doing wrong? I need to animate that object from some matrix state to another (ideally the same one for every object). Is it somehow possible? Like I can specify start transform attribute somehow?
Thanks.
According to Ian's advice, I've used toTransformString:
object.animate({transform: matrix.toTransformString()}, time);
but of course, I had to use it in previous transformations too using
object.attr({transform: added.toTransformString()});//this
//object.transform(added);//instead of this
However, getting local matrix still works as expected. Animation now works and I can use matrix.translate() - to relative move the object or object.animate({transform: "t100,100"}, time).
I also can modify a,b,c,d,e,f attributes of the matrix directly. (or use transform: "T100,100")
It works!
Thanks!

Change water color in d3 orthographic transition map

I am new to D3 and this is my first question to stackoverflow.
I am trying to change the color of the water in this example that contains transitions:
http://bl.ocks.org/mbostock/4183330
I am able to change the color in this example, which is static: http://bl.ocks.org/mbostock/3757125
I found this thread: How can I color ocean with topojson in d3 when I have coordinate info for land? However, this changes the area outside of the globe as well.
This section of the code appears to be the styling, but I can't figure what to add to change the water color.
c.fillStyle = "#bbb", c.beginPath(), path(land), c.fill();
c.fillStyle = "#f00", c.beginPath(), path(countries[i]), c.fill();
c.strokeStyle = "#fff", c.lineWidth = .5, c.beginPath(), path(borders), c.stroke();
c.strokeStyle = "#000", c.lineWidth = 2, c.beginPath(), path(globe), c.stroke();
Also, seeing the line of code below, I searched online for a reference list of possible topojson features and/or objects that might indicate water, and maybe I could figure out how to style that, but couldn't find one:
land = topojson.feature(world, world.objects.land),
I'm wondering if maybe this has something to do with canvas (which I don't really grasp).
Hopefully, I'm overlooking something obvious and noob-like.
Thanks much!
Ha! Of course:
Modify this line:
c.strokeStyle = "#ccc", c.lineWidth = .5 * ratio, c.beginPath(), path(globe), c.stroke();
To this:
c.fillStyle = "#000", c.beginPath(), path(globe), c.fill();
I feel silly, but I guess sometimes it takes writing it all out for the brain cells to click. Thanks!

Resources