Skip to content

Commit 4e2302b

Browse files
committed
Make VR Image aspect ratio configurable with portrait override
VR Images previously used fixed aspect ratios based on content width (0.5 for full width, 0.75 for others). Users can now configure explicit aspect ratios while maintaining backwards compatibility through an Auto option that preserves the original behavior. Portrait orientation support allows different aspect ratios for mobile devices, following the same pattern as iframe embeds. This gives content creators better control over how VR panoramas display across different viewport orientations and sizes. REDMINE-21041
1 parent 175a9f4 commit 4e2302b

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)