communication-react-19
Version:
React library for building modern communication user experiences utilizing Azure Communication Services (React 19 compatible fork)
205 lines • 5.71 kB
JavaScript
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { keyframes, memoizeFunction } from '@fluentui/react';
/**
* Generate random float between two numbers, including min and max
* @private
*/
export function getRandomFloat(minValue, maxValue) {
return minValue + Math.random() * (maxValue - minValue);
}
/**
* Generate random int between two numbers, including min and max
* @private
*/
export function getRandomInt(minValue, maxValue) {
return Math.floor(getRandomFloat(minValue, maxValue + 1));
}
/**
* Calculate the start position for a new reaction in the prescriptive wave pattern
* @private
*/
export function generateStartPositionWave(index, halfCanvasWidth, isOriginAtCanvasCenter = true) {
const midPointCoordinate = isOriginAtCanvasCenter ? halfCanvasWidth : 0;
// If the # of reactions on the screen is 0 or a multiple of 25, then we set direction to 0.
// Otherwise, every other reaction goes right, left in alternating directions.
// To get alternating sequence, we take n % 2 (which gives 0 or 1), multiple result by 2
// and subtract 1, which will result in -1 or 1
const direction = index === 0 ? 0 : (index % 2) * 2 - 1;
if (direction === 0) {
return midPointCoordinate;
}
// Now we get how far the reaction starts from center
const adjustment = scaleStartPos(index);
return midPointCoordinate + direction * adjustment * halfCanvasWidth;
}
/**
* @private
*/
export const reactionOverlayStyle = {
bottom: '0',
height: '50%',
pointerEvents: 'none',
position: 'absolute',
width: '100%'
};
/**
* @private
*/
export function getReactionMovementStyle(reactionXPoint) {
return {
position: 'absolute',
left: `${reactionXPoint}px`
};
}
/**
* Scale metric to determine the start position of a reaction in presentation mode to avoid overlap.
* @private
*/
function scaleStartPos(index) {
switch (index) {
case 1:
case 2:
return 0.3;
case 3:
case 4:
return 0.6;
case 5:
case 6:
return 0.9;
case 7:
case 8:
return 0.75;
case 9:
case 18:
return 0.375;
case 10:
case 23:
return 0.525;
case 11:
case 16:
return 0.15;
case 12:
case 15:
return 0.225;
case 13:
case 14:
return 0.075;
case 17:
case 24:
return 0.45;
case 19:
case 20:
return 0.675;
case 21:
case 22:
return 0.825;
default:
return 0;
}
}
/**
* We have only one bucket item for presentation style of the reaction animation.
* We are choosing to keep the array so that, in future, with styles needed to get updated, one
* can add new styles and apply from here, rather than updating over the same style. Later we can remove
* the old ones.
* It is for the ease of testing and implementation.
* @private
*/
const ReactionStyleBucket = {
sizeScale: 0.9,
heightMaxScale: 0.7 * 0.95,
opacityMax: 0.9
};
/**
* Return a style bucket based on the number of active sprites.
* For example, the first three reactions should appear at maximum
* height, width, and opacity.
* @private
*/
export function getReactionStyleBucket() {
// Having dynamic emoji size on rendering animation impacts performance of the animation itself.
// So we are choosing to use a fixed size for all cases.
return ReactionStyleBucket;
}
/**
* @private
*/
export const moveFrames = memoizeFunction((maxHeight, travelHeight) => keyframes({
'0%': {
transform: `translateY(${maxHeight}px)`
},
'100%': {
transform: `translateY(${travelHeight}px)`
}
}));
/**
* @private
*/
export const moveAnimationStyles = (maxHeight, travelHeight) => {
return {
animationName: moveFrames(maxHeight, travelHeight),
animationFillMode: 'forwards',
animationDuration: `4.133s`,
animationTimingFunction: 'cubic-bezier(0, 0.83, 0.19, 1.09)'
};
};
/**
* @private
*/
export const opacityTransition = memoizeFunction((maxOpacity) => keyframes({
'0%': {
opacity: 0
},
'31.2%': {
opacity: maxOpacity
},
'67.2%': {
opacity: maxOpacity
},
'100%': {
opacity: 0,
display: 'none',
visibility: 'hidden'
}
}));
/**
* @private
*/
export const opacityAnimationStyles = (maxOpacity) => {
return {
animationName: opacityTransition(maxOpacity),
animationFillMode: 'forwards',
animationDuration: `4.133s`
};
};
/**
* @private
*/
export const spriteFrames = memoizeFunction((numOfFrames, displaySizePx) => keyframes({
from: {
backgroundPosition: '0px 0px'
},
to: {
backgroundPosition: `0px -${numOfFrames * displaySizePx}px`
}
}));
/**
* @private
*/
export const spriteAnimationStyles = (numOfFrames, displaySizePx, imageUrl) => {
return {
height: `${displaySizePx}px`,
width: `${displaySizePx}px`,
backgroundImage: `url(${imageUrl})`,
backgroundRepeat: 'no-repeat',
animationName: spriteFrames(numOfFrames, displaySizePx),
animationDuration: `${numOfFrames / 24}s`,
animationFillMode: `forwards`,
animationIterationCount: 'infinite',
animationTimingFunction: `steps(${numOfFrames})`,
backgroundSize: `${displaySizePx}px ${numOfFrames * displaySizePx}px`,
transform: `scale(${displaySizePx}/128)`
};
};
//# sourceMappingURL=ReactionOverlay.style.js.map