Skip to content

Commit d828e3b

Browse files
author
Sepand Parhami
committed
Address review comments.
1 parent 97e4e4b commit d828e3b

File tree

8 files changed

+77
-48
lines changed

8 files changed

+77
-48
lines changed

src/img-dimensions.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,16 @@
3636
}
3737

3838
/**
39-
* Constrains the size of the image to the given width an height. This either
39+
* Constrains the size of the image to the given width and height. This either
4040
* caps the width or the height depending on the aspect ratio of original img
4141
* and if we want to have the smaller or larger dimension fit the container.
42+
* @param naturalSize The natural dimensions of the image.
43+
* @param containerSize The size of the container we want to render the image
44+
* in.
45+
* @param toMin If we whould cap the smaller dimension of the image to fit the
46+
* container (`object-fit: cover`) or the larger dimension
47+
* (`object-fit: contain`).
48+
* @return The Size that the image should be rendered as.
4249
*/
4350
function constrain(
4451
naturalSize: Size, containerSize: Size, toMin: boolean): Size {

src/replacement-img.ts renamed to src/imitation-img.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ import {getRenderedDimensions} from './img-dimensions.js';
2626
* @param srcImgRect
2727
* @return The replacement container along with structural information.
2828
*/
29-
export function createReplacement(
29+
export function createImitationImg(
3030
srcImg: HTMLImageElement,
3131
srcImgRect: ClientRect = srcImg.getBoundingClientRect(),
3232
): {
33-
translateElement,
33+
translateElement: HTMLElement,
3434
scaleElement: HTMLElement,
3535
counterScaleElement: HTMLElement,
3636
img: HTMLImageElement,

src/test-replacement-img.ts renamed to src/test-imitation-img.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
* limitations under the License.
1515
*/
1616

17-
import {createReplacement} from './replacement-img.js';
18-
import {imgLoadPromise} from './testing/utils.js';
17+
import {createImitationImg} from './imitation-img';
18+
import {imgLoadPromise} from './testing/utils';
1919

2020
const {expect} = chai;
2121
const fourByThreeUri = 'data:image/gif;base64,R0lGODdhBAADAIAAAP///////ywAAAAABAADAAACA4SPVgA7';
@@ -27,7 +27,7 @@ async function updateImg(img, fit, src) {
2727
await imgLoadPromise(img);
2828
}
2929

30-
describe('createReplacement', () => {
30+
describe('createImitationImg', () => {
3131
let testContainer;
3232
let img;
3333

@@ -51,7 +51,7 @@ describe('createReplacement', () => {
5151

5252
describe('the scaleElement', () => {
5353
it('should size correctly', async () => {
54-
const {scaleElement} = createReplacement(img);
54+
const {scaleElement} = createImitationImg(img);
5555
testContainer.appendChild(scaleElement);
5656

5757
const {width, height} = scaleElement.getBoundingClientRect();
@@ -65,7 +65,7 @@ describe('createReplacement', () => {
6565
let replacementImg;
6666

6767
beforeEach(() => {
68-
const replacement = createReplacement(img);
68+
const replacement = createImitationImg(img);
6969
scaleElement = replacement.scaleElement;
7070
replacementImg = replacement.img;
7171
testContainer.appendChild(scaleElement);
@@ -100,7 +100,7 @@ describe('createReplacement', () => {
100100
let replacementImg;
101101

102102
beforeEach(() => {
103-
const replacement = createReplacement(img);
103+
const replacement = createImitationImg(img);
104104
const scaleElement = replacement.scaleElement;
105105
replacementImg = replacement.img;
106106
testContainer.appendChild(scaleElement);
@@ -134,7 +134,7 @@ describe('createReplacement', () => {
134134
let replacementImg;
135135

136136
beforeEach(() => {
137-
const replacement = createReplacement(img);
137+
const replacement = createImitationImg(img);
138138
const scaleElement = replacement.scaleElement;
139139
replacementImg = replacement.img;
140140
testContainer.appendChild(scaleElement);

src/testing/utils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
* limitations under the License.
1515
*/
1616

17+
/**
18+
* @param img An img to wait for.
19+
* @return A Promise that resolves once the image has loaded.
20+
*/
1721
export async function imgLoadPromise(img: HTMLImageElement): Promise<void> {
1822
if (img.complete) {
1923
return;

src/transform-img/README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,20 @@ Each of these are implemented in seprate file.
1414
## Implementation
1515

1616
In order to have the animation perform well, including on older devices, only
17-
CPU accelrated animatable properties. As a result, the 'crop' portion of the
17+
CPU accelerated animatable properties. As a result, the 'crop' portion of the
1818
animation is implemented by scaling up a cropping container (with
1919
`overflow: hidden`) and counteracting the scaling with an inverse scale on a
20-
child container.
20+
child container. This cannot be done easily with a CSS timing function, so a
21+
stylesheet with keyframes is generated for the crop scale / counteracting scale
22+
values.
23+
24+
The code attempts to generate enough keyframes to keep any errors due to
25+
interpolation during the animation low, but at the same time avoid doing more
26+
work than necessary.
27+
28+
Both the scaling of the image itself as well as the translate can be done
29+
directly with a CSS timing function, so those two operations do not generate
30+
keyframes.
2131

2232
The animations are implemented by taking the larger size and animating using
2333
that as a reference point for scaling. That is, when the transition is where

src/transform-img/crop-animation.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ interface Scale {
3535
const numSamples = 20;
3636

3737
/**
38-
* Interpolates a value x% between b and a.
38+
* Interpolates a value x% between a and b.
3939
*/
4040
function interpolate(a: number, b: number, x: number): number {
41-
return b + x * (a - b);
41+
return a + x * (b - a);
4242
}
4343

4444
/**
@@ -75,8 +75,8 @@ function generateCropKeyframes({
7575
// The output percentage at this point.
7676
const py = getBezierCurveValue(curve.y1, curve.y2, t);
7777
const keyframePercentage = px * 100;
78-
const scaleX = interpolate(endScale.x, startScale.x, py);
79-
const scaleY = interpolate(endScale.y, startScale.y, py);
78+
const scaleX = interpolate(startScale.x, endScale.x, py);
79+
const scaleY = interpolate(startScale.y, endScale.y, py);
8080
const counterScaleX = 1 / scaleX;
8181
const counterScaleY = 1 / scaleY;
8282

src/transform-img/transform-img.ts

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,16 @@
1616

1717
import {Curve} from '../bezier-curve-utils.js';
1818
import {getRenderedDimensions} from '../img-dimensions.js';
19-
import {createReplacement} from '../replacement-img.js';
19+
import {createImitationImg} from '../imitation-img.js';
2020
import {prepareCropAnimation} from './crop-animation.js';
2121
import {prepareScaleAnimation} from './scale-animation.js';
2222
import {prepareTranslateAnimation} from './translate-animation.js';
2323

24+
/**
25+
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/single-transition-timing-function#ease-in-out
26+
*/
27+
const EASE_IN_OUT = {x1: 0.42, y1: 0, x2: 0.58, y2: 1};
28+
2429
/**
2530
* A counter that makes sure the keyframes names are unique.
2631
*/
@@ -39,33 +44,34 @@ function getKeyframesPrefix(namespace: string): string {
3944
}
4045

4146
/**
42-
* Prepares an animation from one image to another. Creates a a temporary
43-
* replacement image that is transitioned between the two images.
47+
* Prepares an animation from one image to another. Creates a temporary
48+
* transition image that is transitioned between the position, size and crop of
49+
* two images.
4450
* @param options
4551
* @param options.transitionContainer The container to place the transition
46-
* image in. Defaults to document.body.
52+
* image in. This could be useful if you need to place the transition in a
53+
* container with a specific `z-index` to appear on top of other elements in
54+
* the page. It can also be useful if you want to have the transition stay
55+
* within the `ShadowRoot` of a component. Defaults to document.body.
4756
* @param options.styleContainer The container to place the generated
4857
* stylesheet in. Defaults to document.head.
4958
* @param options.srcImg The image to transition from.
5059
* @param options.targetImg The image to transition to.
5160
* @param options.srcImgRect The ClientRect for where the transition should
52-
* start from. Defaults to the current rect for srcImg.
61+
* start from. Specifying this could be useful if you need to hide
62+
* (`display: none`) the container for `srcImg` in order to layout a page
63+
* containing `targetImg`. In that case, you can capture the rect for
64+
* `srcImg` up front and then pass it. Defaults to the current rect for
65+
* srcImg.
5366
* @param options.targetImgRect The ClientRect for where the transition should
54-
* end at. Defaults to the current rect for targetImg.
67+
* end at. Specifying this could be useful if you have not had a chance to
68+
* perform the layout for the target yet (e.g. in a `display: none`
69+
* container), but you know where on the screen it will go. Defaults to the
70+
* current rect for targetImg.
5571
* @param options.curve Control points for a Bezier curve to use for the
5672
* animation.
5773
* @param options.styles Styles to apply to the transitioning Elements. This
5874
* should include animationDuration. It might also include animationDelay.
59-
* @param options.translateCurve Control points for a Bezier curve to use for
60-
* the translation portion of animation. Defaults to `curve`.
61-
* @param options.translateStyles Styles to apply to the translating Elements.
62-
* This should include animationDuration. It might also include
63-
* animationDelay. Defaults to `styles`.
64-
* @param options.scaleCurve Control points for a Bezier curve to use for
65-
* the translation portion of animation. Defaults to `curve`.
66-
* @param options.scaleStyles Styles to apply to the scaling Elements. This
67-
* should include animationDuration. It might also include animationDelay.
68-
* Defaults to `styles`.
6975
* @param options.keyframesNamespace A namespace to use for the generated
7076
* keyframes to ensure they do not clash with existing keyframes.
7177
*/
@@ -76,12 +82,8 @@ export function prepareImageAnimation({
7682
targetImg,
7783
srcImgRect = srcImg.getBoundingClientRect(),
7884
targetImgRect = targetImg.getBoundingClientRect(),
79-
curve,
85+
curve = EASE_IN_OUT,
8086
styles,
81-
translateCurve = curve,
82-
translateStyles = styles,
83-
scaleCurve = curve,
84-
scaleStyles = styles,
8587
keyframesNamespace = 'img-transform',
8688
} : {
8789
transitionContainer: HTMLElement,
@@ -90,12 +92,8 @@ export function prepareImageAnimation({
9092
targetImg: HTMLImageElement,
9193
srcImgRect?: ClientRect,
9294
targetImgRect?: ClientRect,
93-
curve: Curve,
95+
curve?: Curve,
9496
styles: Object,
95-
translateCurve?: Curve,
96-
translateStyles?: Object,
97-
scaleCurve?: Curve,
98-
scaleStyles?: Object,
9997
keyframesNamespace?: string,
10098
}) : {
10199
applyAnimation: () => void,
@@ -118,7 +116,7 @@ export function prepareImageAnimation({
118116
img,
119117
imgWidth: largerImgWidth,
120118
imgHeight: largerImgHeight,
121-
} = createReplacement(largerImg, largerRect);
119+
} = createImitationImg(largerImg, largerRect);
122120
const {
123121
width: smallerImgWidth,
124122
height: smallerImgHeight,
@@ -138,8 +136,8 @@ export function prepareImageAnimation({
138136
element: translateElement,
139137
largerRect,
140138
smallerRect,
141-
curve: translateCurve,
142-
styles: translateStyles,
139+
curve,
140+
styles,
143141
keyframesPrefix,
144142
toLarger,
145143
});
@@ -149,8 +147,8 @@ export function prepareImageAnimation({
149147
largerImgHeight,
150148
smallerImgWidth,
151149
smallerImgHeight,
152-
curve: scaleCurve,
153-
styles: scaleStyles,
150+
curve,
151+
styles,
154152
keyframesPrefix,
155153
toLarger,
156154
});

src/transform-img/translate-animation.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@
1616

1717
import {Curve, curveToString} from '../bezier-curve-utils.js';
1818

19+
/**
20+
* Prepares a translation animation, assuming that `smallerRect` will be scaled
21+
* up to `largerRect` and adjusting the positions to account for the scaling.
22+
* This function sets up the animation by setting the appropriate style
23+
* properties on the desired Element. The returned style text needs to be
24+
* inserted for the animation to run.
25+
* @return CSS style text to perform the aniamtion.
26+
*/
1927
export function prepareTranslateAnimation({
2028
element,
2129
largerRect,
@@ -40,7 +48,9 @@ export function prepareTranslateAnimation({
4048
// We need to calculate the left/top, but account for the fact the
4149
// container will be a different size due to scaling.
4250
const deltaWidth = (endRect.width - startRect.width);
43-
const deltaHeight = (endRect.height - startRect.height)
51+
const deltaHeight = (endRect.height - startRect.height);
52+
// The larger rect will always have a scaling of 1:1, so we do not need to
53+
// modify the position if our start/end is th larger rect.
4454
const startLeft = startRect.left - (toLarger ? deltaWidth / 2 : 0);
4555
const startTop = startRect.top - (toLarger ? deltaHeight / 2 : 0);
4656
const endLeft = endRect.left + (toLarger ? 0 : deltaWidth / 2);

0 commit comments

Comments
 (0)