Replies: 3 comments 5 replies
-
Thanks you for the kind words 🙌🏼 This is kind of example we love investigate. Glancing at the code, there seems like there is a lot happening on every single frame, there might be a way to reduce the number of instructions that need to be executed. Let's make the logic of this example super simple. From there, there are other things we can do to optimize for such an example but first we need to work on a lean and clean example. But this looks fun, happy hacking :) |
Beta Was this translation helpful? Give feedback.
-
Hi @wcandillon ! I finaly found some time to refacto the avatar code following your suggestion. As you can see now the logic is much more simpler, and only use 2 derivatedValues : import {Circle, Group} from '@shopify/react-native-skia';
import React from 'react';
import {Dimensions} from 'react-native';
import {
SharedValue,
interpolate,
useDerivedValue,
} from 'react-native-reanimated';
import {Extrapolation} from 'react-native-reanimated';
type SkAvatarProps = {
x: SharedValue<number>;
y: SharedValue<number>;
color?: string;
index: number;
positions: {
x: number;
y: number;
}[];
canvasHeight: number;
};
export const AVATAR_SIZE = 40;
export const EDGE = AVATAR_SIZE + 8;
const CANVAS_WIDTH = Dimensions.get('window').width;
export const SkAvatar = ({
x,
y,
color = 'mediumpurple',
index,
positions,
canvasHeight,
}: SkAvatarProps) => {
const CANVAS_HEIGHT = canvasHeight;
const INNER_ZONE_X_RADIUS = CANVAS_WIDTH / 2 - EDGE;
const INNER_ZONE_Y_RADIUS = CANVAS_HEIGHT / 2 - EDGE;
/////////// CALCULATED TRANSFORM
const calculatedTransform = useDerivedValue(() => {
const dx = x.value;
const dy = y.value;
const avatarX = positions[index].x + x.value - CANVAS_WIDTH / 2;
const avatarY = positions[index].y + y.value - CANVAS_HEIGHT / 2;
let isCornerRegion = false;
// parameters
let avatarScale = 1;
let translateX = dx;
let translateY = dy;
// Check if bubble is in innerZonne
if (
Math.abs(avatarX) <= INNER_ZONE_X_RADIUS &&
Math.abs(avatarY) <= INNER_ZONE_Y_RADIUS
) {
// Check if bubble is in innerZonne
isCornerRegion = false;
avatarScale = 1;
} else {
// Check if bubble is in corner zone
if (
Math.abs(avatarX) > INNER_ZONE_X_RADIUS &&
Math.abs(avatarY) > INNER_ZONE_Y_RADIUS
) {
isCornerRegion = true;
avatarScale = interpolate(
Math.sqrt(
Math.pow(Math.abs(avatarX) - INNER_ZONE_X_RADIUS, 2) +
Math.pow(Math.abs(avatarY) - INNER_ZONE_Y_RADIUS, 2),
),
[0, EDGE],
[1, 0],
Extrapolation.CLAMP,
);
} else {
// Bubble not in corner zone
isCornerRegion = false;
avatarScale = interpolate(
Math.max(
Math.abs(avatarX) - INNER_ZONE_X_RADIUS,
Math.abs(avatarY) - INNER_ZONE_Y_RADIUS,
),
[0, EDGE],
[1, 0],
Extrapolation.CLAMP,
);
}
}
if (isCornerRegion) {
const cornerDx = Math.abs(avatarX) - INNER_ZONE_X_RADIUS;
const cornerDy = Math.abs(avatarY) - INNER_ZONE_Y_RADIUS;
let theta = Math.atan(-cornerDy / cornerDx);
if (avatarX > 0) {
if (avatarY > 0) {
theta *= -1;
}
} else {
if (avatarY > 0) {
theta += Math.PI;
} else {
theta += Math.PI - 2 * theta;
}
}
translateX -= AVATAR_SIZE * (1 - avatarScale) * Math.cos(theta);
translateY -= AVATAR_SIZE * (1 - avatarScale) * Math.sin(theta);
} else if (
Math.abs(avatarX) > INNER_ZONE_X_RADIUS ||
Math.abs(avatarY) > INNER_ZONE_X_RADIUS
) {
if (Math.abs(avatarX) > INNER_ZONE_X_RADIUS) {
translateX -= AVATAR_SIZE * (1 - avatarScale) * Math.sign(avatarX);
} else {
translateY -= AVATAR_SIZE * (1 - avatarScale) * Math.sign(avatarY);
}
}
return [
{scale: avatarScale},
{translateX: translateX},
{translateY: translateY},
];
});
///////////////// ORIGIN POINT
const origin = useDerivedValue(() => {
return {
x: positions[index].x + calculatedTransform.value[1].translateX!,
y: positions[index].y + calculatedTransform.value[2].translateY!,
};
});
return (
<Group origin={origin} transform={calculatedTransform}>
<Circle
cx={positions[index].x}
cy={positions[index].y}
r={AVATAR_SIZE}
color={color}
/>
</Group>
);
}; I couldn't find a real solution to calculate the origin point so I used kind of hacky solution, if you have a better way of doing this I'll be glad to hear it Anyway the results are slightly better than the previous iterations, but unfortunately we are still below 30FPS : Enregistrement.de.l.ecran.2023-06-26.a.12.33.08.movI don't see where I can reduce the number of instructions in a frame, do you see where I can improve the performance ? Thank you again for your work and you support, best regards 🥰 |
Beta Was this translation helpful? Give feedback.
-
Hi @wcandillon ! I tried this morning with animated views, which gives me a much worse result : with_animated_view.mov(has you can see I couldn't reproduce the exact effect but I don't think it's relevant) I also tried to with the latest version of reanimated in the hope of some improvements : with_reanimated_3.4.0.movStill I couldn't see a noticeable improvement. But from what I can see skia is such an improvement for those type of animations, such a nice work 🙌 |
Beta Was this translation helpful? Give feedback.
-
Hi everyone !
First of all I wanted to thank all the developers who contributed to this absolutely amazing library ! I'm still amazed by the capabilities and can't wait to see what people are going to do with it.
I was so excited in fact that I decided to use it on a side project. The idea of this project was to more or less mimic the apple watch UI.
For that I needed to render approximately 50 "AVATARS" on screen.
Everything works perfectly on iOS (of course), but I tested on an Samsung A41 (somewhat of low end device), and I found that the FPS where dropping by half :
Enregistrement.de.l.ecran.2023-06-08.a.17.56.48.mov
This recording was taken in release mode.
What I also don't understand is that a JS thread is also dropping frame and I'm really not sure why....
So I just wanted to ask you guys what is wrong with my code, and what could I do to improve those performance ?
My Canvas :
And My "Avatar" :
you can also find the full repo here : repro
Once again thank you very much for all your work, and for even looking at my post 🫶 (first time ever posting on github what a milestone haha)
Best Regards,
Beta Was this translation helpful? Give feedback.
All reactions