Skip to content

Commit acb7aa6

Browse files
authored
Merge pull request #2243 from tf/vr-aspect-ratio
Make VR Image aspect ratio configurable with portrait override
2 parents 7a2310b + 4e2302b commit acb7aa6

File tree

8 files changed

+251
-5
lines changed

8 files changed

+251
-5
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
de:
2+
pageflow_scrolled:
3+
editor:
4+
content_elements:
5+
vrImage:
6+
attributes:
7+
aspectRatio:
8+
blank: "(Automatisch)"
9+
inline_help: Bestimmt die Form des VR-Panorama-Viewports. Automatisch verwendet optimierte Verhältnisse basierend auf der Elementbreite.
10+
label: Seitenverhältnis
11+
values:
12+
narrow: Querformat (4:3)
13+
portrait: Hochformat (9:16)
14+
square: Quadrat (1:1)
15+
wide: Querformat (16:9)
16+
portraitAspectRatio:
17+
blank: "(Standard)"
18+
inline_help: Wird angezeigt, wenn das Browser-Viewport höher als breit ist, beispielsweise auf Telefonen oder Tablets im Hochformat. Kann verwendet werden, um ein anderes Seitenverhältnis bereitzustellen, das für die mobile Ansicht optimiert ist.
19+
label: Seitenverhältnis (Hochkant)
20+
values:
21+
narrow: Querformat (4:3)
22+
portrait: Hochformat (9:16)
23+
square: Quadrat (1:1)
24+
wide: Querformat (16:9)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
en:
2+
pageflow_scrolled:
3+
editor:
4+
content_elements:
5+
vrImage:
6+
attributes:
7+
aspectRatio:
8+
blank: "(Auto)"
9+
inline_help: Control the shape of the VR panorama viewport. Auto uses optimized ratios based on element width.
10+
label: Aspect Ratio
11+
values:
12+
narrow: Landscape (4:3)
13+
portrait: Portrait (9:16)
14+
square: Square (1:1)
15+
wide: Landscape (16:9)
16+
portraitAspectRatio:
17+
blank: "(Default)"
18+
inline_help: Displayed when the browser viewport is taller than wide, for example on phones or tablets in portrait orientation. Can be used to provide a different aspect ratio optimized for mobile viewing.
19+
label: Aspect Ratio (Portrait)
20+
values:
21+
narrow: Landscape (4:3)
22+
portrait: Portrait (9:16)
23+
square: Square (1:1)
24+
wide: Landscape (16:9)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
node_modules
22
/editor.js
3-
/frontend.js
3+
/frontend
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import {getAspectRatio} from 'contentElements/vrImage/getAspectRatio';
2+
import {contentElementWidths} from 'pageflow-scrolled/frontend';
3+
4+
describe('getAspectRatio', () => {
5+
describe('when aspectRatio is not set (auto behavior)', () => {
6+
it('returns 0.5 for full width', () => {
7+
const result = getAspectRatio({
8+
configuration: {},
9+
contentElementWidth: contentElementWidths.full,
10+
portraitOrientation: false
11+
});
12+
13+
expect(result).toEqual(0.5);
14+
});
15+
16+
it('returns 0.75 for non-full width', () => {
17+
const result = getAspectRatio({
18+
configuration: {},
19+
contentElementWidth: contentElementWidths.md,
20+
portraitOrientation: false
21+
});
22+
23+
expect(result).toEqual(0.75);
24+
});
25+
26+
it('returns 0.75 for non-full width when aspectRatio is null', () => {
27+
const result = getAspectRatio({
28+
configuration: {aspectRatio: null},
29+
contentElementWidth: contentElementWidths.md,
30+
portraitOrientation: false
31+
});
32+
33+
expect(result).toEqual(0.75);
34+
});
35+
});
36+
37+
describe('when aspectRatio is set to specific value', () => {
38+
it('returns correct ratio for wide', () => {
39+
const result = getAspectRatio({
40+
configuration: {aspectRatio: 'wide'},
41+
contentElementWidth: contentElementWidths.md,
42+
portraitOrientation: false
43+
});
44+
45+
expect(result).toEqual(0.5625);
46+
});
47+
48+
it('returns correct ratio for narrow', () => {
49+
const result = getAspectRatio({
50+
configuration: {aspectRatio: 'narrow'},
51+
contentElementWidth: contentElementWidths.md,
52+
portraitOrientation: false
53+
});
54+
55+
expect(result).toEqual(0.75);
56+
});
57+
58+
it('returns correct ratio for square', () => {
59+
const result = getAspectRatio({
60+
configuration: {aspectRatio: 'square'},
61+
contentElementWidth: contentElementWidths.md,
62+
portraitOrientation: false
63+
});
64+
65+
expect(result).toEqual(1);
66+
});
67+
68+
it('returns correct ratio for portrait', () => {
69+
const result = getAspectRatio({
70+
configuration: {aspectRatio: 'portrait'},
71+
contentElementWidth: contentElementWidths.md,
72+
portraitOrientation: false
73+
});
74+
75+
expect(result).toEqual(1.7777);
76+
});
77+
78+
it('returns 0.75 for unknown aspect ratio', () => {
79+
const result = getAspectRatio({
80+
configuration: {aspectRatio: 'unknown'},
81+
contentElementWidth: contentElementWidths.md,
82+
portraitOrientation: false
83+
});
84+
85+
expect(result).toEqual(0.75);
86+
});
87+
88+
it('uses specific aspect ratio even for full width', () => {
89+
const result = getAspectRatio({
90+
configuration: {aspectRatio: 'square'},
91+
contentElementWidth: contentElementWidths.full,
92+
portraitOrientation: false
93+
});
94+
95+
expect(result).toEqual(1);
96+
});
97+
});
98+
99+
describe('portrait orientation behavior', () => {
100+
it('uses portraitAspectRatio when in portrait orientation', () => {
101+
const result = getAspectRatio({
102+
configuration: {
103+
aspectRatio: 'wide',
104+
portraitAspectRatio: 'square'
105+
},
106+
contentElementWidth: contentElementWidths.md,
107+
portraitOrientation: true
108+
});
109+
110+
expect(result).toEqual(1);
111+
});
112+
113+
it('uses portraitAspectRatio even when main aspectRatio is not set', () => {
114+
const result = getAspectRatio({
115+
configuration: {
116+
portraitAspectRatio: 'portrait'
117+
},
118+
contentElementWidth: contentElementWidths.md,
119+
portraitOrientation: true
120+
});
121+
122+
expect(result).toEqual(1.7777);
123+
});
124+
125+
126+
it('falls back to main aspectRatio when portraitAspectRatio is not set', () => {
127+
const result = getAspectRatio({
128+
configuration: {aspectRatio: 'square'},
129+
contentElementWidth: contentElementWidths.md,
130+
portraitOrientation: true
131+
});
132+
133+
expect(result).toEqual(1);
134+
});
135+
136+
it('falls back to auto behavior when neither is set in portrait', () => {
137+
const result = getAspectRatio({
138+
configuration: {},
139+
contentElementWidth: contentElementWidths.full,
140+
portraitOrientation: true
141+
});
142+
143+
expect(result).toEqual(0.5);
144+
});
145+
});
146+
});

entry_types/scrolled/package/src/contentElements/vrImage/VrImage.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import React, {useRef, useState} from 'react';
22
import {useAutoCruising} from './useAutoCruising';
3+
import {getAspectRatio} from './getAspectRatio';
34

45
import {
5-
contentElementWidths,
66
useContentElementEditorState,
77
useContentElementLifecycle,
88
useFileWithInlineRights,
9+
usePortraitOrientation,
910
ContentElementBox,
1011
ContentElementFigure,
1112
Panorama,
@@ -16,22 +17,25 @@ import {
1617
export function VrImage({configuration, contentElementWidth}) {
1718
const {shouldLoad} = useContentElementLifecycle();
1819
const {isEditable, isSelected} = useContentElementEditorState();
20+
const portraitOrientation = usePortraitOrientation();
1921

2022
const imageFile = useFileWithInlineRights({
2123
configuration,
2224
collectionName: 'imageFiles',
2325
propertyName: 'image'
2426
});
2527

28+
const aspectRatio = getAspectRatio({configuration, contentElementWidth, portraitOrientation});
29+
2630
return (
2731
<div style={{pointerEvents: isEditable && !isSelected ? 'none' : undefined}}>
2832
<FitViewport
29-
aspectRatio={contentElementWidth === contentElementWidths.full ? 0.5 : 0.75}
33+
aspectRatio={aspectRatio}
3034
fill={configuration.position === 'backdrop'}>
3135
<ContentElementBox>
3236
<ContentElementFigure configuration={configuration}>
3337
<FitViewport.Content>
34-
{renderLazyPanorama(configuration, imageFile, shouldLoad)}
38+
{renderLazyPanorama(configuration, imageFile, shouldLoad, aspectRatio)}
3539
<InlineFileRights configuration={configuration}
3640
context="insideElement"
3741
items={[{file: imageFile, label: 'image'}]} />
@@ -46,9 +50,10 @@ export function VrImage({configuration, contentElementWidth}) {
4650
);
4751
}
4852

49-
function renderLazyPanorama(configuration, imageFile, shouldLoad) {
53+
function renderLazyPanorama(configuration, imageFile, shouldLoad, aspectRatio) {
5054
if (shouldLoad && imageFile && imageFile.isReady) {
5155
return (<AutoCruisingPanorama imageFile={imageFile}
56+
key={aspectRatio}
5257
initialYaw={configuration.initialYaw}
5358
initialPitch={configuration.initialPitch} />)
5459
}

entry_types/scrolled/package/src/contentElements/vrImage/editor.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import {SelectInputView, FileInputView, EnumTableCellView, SliderInputView, Sepa
33

44
import pictogram from './pictogram.svg';
55

6+
const aspectRatios = ['wide', 'narrow', 'square', 'portrait'];
7+
68
editor.contentElementTypes.register('vrImage', {
79
pictogram,
810
category: 'interactive',
@@ -28,6 +30,21 @@ editor.contentElementTypes.register('vrImage', {
2830
minValue: -60,
2931
maxValue: 60
3032
});
33+
this.input('aspectRatio', SelectInputView, {
34+
includeBlank: true,
35+
blankTranslationKey: 'pageflow_scrolled.editor.' +
36+
'content_elements.vrImage.' +
37+
'attributes.aspectRatio.blank',
38+
values: aspectRatios
39+
});
40+
this.input('portraitAspectRatio', SelectInputView, {
41+
includeBlank: true,
42+
blankTranslationKey: 'pageflow_scrolled.editor.' +
43+
'content_elements.vrImage.' +
44+
'attributes.portraitAspectRatio.blank',
45+
values: aspectRatios
46+
});
47+
this.view(SeparatorView);
3148
this.group('ContentElementPosition');
3249
this.view(SeparatorView);
3350
this.group('ContentElementCaption', {entry});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import {contentElementWidths} from 'pageflow-scrolled/frontend';
2+
3+
const aspectRatios = {
4+
wide: 0.5625,
5+
narrow: 0.75,
6+
square: 1,
7+
portrait: 1.7777
8+
};
9+
10+
export function getAspectRatio({configuration, contentElementWidth, portraitOrientation}) {
11+
const effectiveAspectRatio = portraitOrientation && configuration.portraitAspectRatio
12+
? configuration.portraitAspectRatio
13+
: configuration.aspectRatio;
14+
15+
if (!effectiveAspectRatio) {
16+
return getAutoAspectRatio(contentElementWidth);
17+
}
18+
19+
return aspectRatios[effectiveAspectRatio] || 0.75;
20+
}
21+
22+
function getAutoAspectRatio(contentElementWidth) {
23+
return contentElementWidth === contentElementWidths.full ? 0.5 : 0.75;
24+
}

entry_types/scrolled/package/src/contentElements/vrImage/stories.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ storiesOfContentElement(module, {
2323
initialPitch: 30
2424
}
2525
},
26+
{
27+
name: 'With custom aspect ratio',
28+
configuration: {
29+
aspectRatio: 'square'
30+
}
31+
},
2632
{
2733
name: 'Stereo image',
2834
configuration: {

0 commit comments

Comments
 (0)