From 0f970fa1f164a310e24a85140d8bf0da21e5a5f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thea=20Sch=C3=B6bl?= Date: Thu, 4 May 2023 12:18:48 +0000 Subject: [PATCH] fix: parallax broken since safari 16.4 Moves the parallax effect into a directive which injects required elements into the shadow DOM and adds `part` attributes, so it can be styled from a global stylesheet. --- src/app/modules/about/about-changelog.html | 8 +- src/app/modules/about/about-licenses.html | 50 +++-- .../modules/about/about-page/about-page.html | 10 +- .../chips/data/add-event-action-chip.html | 16 +- .../chips/data/add-event-action-chip.scss | 5 - src/app/modules/data/detail/data-detail.html | 8 +- src/app/modules/data/detail/data-path.scss | 5 - src/app/modules/feedback/feedback-page.html | 200 +++++++++--------- src/app/modules/news/page/news-page.html | 70 +++--- .../modules/profile/page/profile-page.html | 182 ++++++++-------- .../modules/profile/page/profile-page.scss | 8 - .../modules/settings/page/settings-page.html | 34 ++- .../util/ion-content-parallax.directive.ts | 116 ++++++++++ src/app/util/util.module.ts | 3 + src/global.scss | 4 - src/theme/common/_ion-content-parallax.scss | 67 +++--- 16 files changed, 428 insertions(+), 358 deletions(-) create mode 100644 src/app/util/ion-content-parallax.directive.ts diff --git a/src/app/modules/about/about-changelog.html b/src/app/modules/about/about-changelog.html index e0f1aa09..61d385b6 100644 --- a/src/app/modules/about/about-changelog.html +++ b/src/app/modules/about/about-changelog.html @@ -22,10 +22,8 @@ - -
-
- -
+ +
+
diff --git a/src/app/modules/about/about-licenses.html b/src/app/modules/about/about-licenses.html index 9b5b2612..e8562129 100644 --- a/src/app/modules/about/about-licenses.html +++ b/src/app/modules/about/about-licenses.html @@ -22,32 +22,30 @@ - -
-
- - - - {{ license.name }} - - + +
+ + + + {{ license.name }} + + - - {{ license.authors || license.publisher }} - - - - - - {{ license.licenses }} License - - - -
+ + {{ license.authors || license.publisher }} + +
+ + + + {{ license.licenses }} License + + +
diff --git a/src/app/modules/about/about-page/about-page.html b/src/app/modules/about/about-page/about-page.html index 89b8b031..5226594f 100644 --- a/src/app/modules/about/about-page/about-page.html +++ b/src/app/modules/about/about-page/about-page.html @@ -24,11 +24,9 @@ - -
- {{ appName }} v{{ version }} -
- -
+ + {{ appName }} v{{ version }} +
+
diff --git a/src/app/modules/data/chips/data/add-event-action-chip.html b/src/app/modules/data/chips/data/add-event-action-chip.html index d5f0bc3e..1f09b1a6 100644 --- a/src/app/modules/data/chips/data/add-event-action-chip.html +++ b/src/app/modules/data/chips/data/add-event-action-chip.html @@ -1,5 +1,5 @@ - - - + + + - + - - {{ 'settings.resetSettings' | translate }} - - -
+ + {{ 'settings.resetSettings' | translate }} + +
diff --git a/src/app/util/ion-content-parallax.directive.ts b/src/app/util/ion-content-parallax.directive.ts new file mode 100644 index 00000000..1182e386 --- /dev/null +++ b/src/app/util/ion-content-parallax.directive.ts @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2023 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +import {Directive, ElementRef, HostBinding, Input, OnDestroy, OnInit} from '@angular/core'; + +type IonicColor = + | 'danger' + | 'dark' + | 'light' + | 'medium' + | 'primary' + | 'secondary' + | 'success' + | 'tertiary' + | 'warning' + | string; + +/** + * Adds a parallax effect to `` elements + * + * Why is this necessary and why do we need to inject + * elements into a foreign element's shadow DOM just for this? + * + * We previously had used a global style in conjunction with + * adding a child `
`, however, unfortunately Safari 16.4 + * broke 3D transforms where the parent is inside the shadow + * DOM and child elements are outside. + * + * We also have to use `` as ionic relies on the + * `` selector to make iOS animations work. + * + * Which means this is pretty much the only way to solve this + * problem. + * + * This directive, when applied using ``, + * will transform its shadow DOM from + * + * ```html + *
+ *
+ * + *
+ * + * ``` + * + * To + * + * ```html + *
+ *
+ *
+ *
+ * + *
+ *
+ * + * ``` + * + * Which we can then be used through `::part()` to style it from + * a global style sheet. + */ +@Directive({ + selector: 'ion-content[parallax]', +}) +export class IonContentParallaxDirective implements OnInit, OnDestroy { + @HostBinding('style.--parallax-content-size.px') @Input() parallaxSize = 230; + + @Input() set parallaxColor(value: IonicColor) { + this.parallaxBackground = `var(--ion-color-${value})`; + } + + @HostBinding('style.--parallax-background') parallaxBackground?: string; + + private mutationObserver: MutationObserver; + + constructor(private element: ElementRef) {} + + ngOnInit() { + this.mutationObserver = new MutationObserver(() => { + const inner = this.element.nativeElement.shadowRoot.querySelector('.inner-scroll') as + | HTMLDivElement + | undefined; + if (!inner) return; + + // eslint-disable-next-line unicorn/no-array-for-each + inner.childNodes.forEach(node => node.remove()); + + inner.part.add('parallax-scroll'); + const parallaxParentElement = document.createElement('div'); + parallaxParentElement.part.add('parallax-parent'); + const parallaxElement = document.createElement('div'); + parallaxElement.part.add('parallax'); + + inner.append(parallaxParentElement); + parallaxParentElement.append(parallaxElement, document.createElement('slot')); + }); + this.mutationObserver.observe(this.element.nativeElement.shadowRoot, { + childList: true, + }); + } + + ngOnDestroy() { + this.mutationObserver.disconnect(); + } +} diff --git a/src/app/util/util.module.ts b/src/app/util/util.module.ts index b9d9300d..b36edc67 100644 --- a/src/app/util/util.module.ts +++ b/src/app/util/util.module.ts @@ -31,10 +31,12 @@ import {SimpleSwiperComponent} from './simple-swiper.component'; import {SearchbarAutofocusDirective} from './searchbar-autofocus.directive'; import {SectionComponent} from './section.component'; import {RouterModule} from '@angular/router'; +import {IonContentParallaxDirective} from './ion-content-parallax.directive'; @NgModule({ imports: [BrowserModule, IonicModule, TranslateModule, ThingTranslateModule.forChild(), RouterModule], declarations: [ + IonContentParallaxDirective, ElementSizeChangeDirective, ArrayLastPipe, DateIsThisPipe, @@ -50,6 +52,7 @@ import {RouterModule} from '@angular/router'; SearchbarAutofocusDirective, ], exports: [ + IonContentParallaxDirective, ElementSizeChangeDirective, ArrayLastPipe, DateIsThisPipe, diff --git a/src/global.scss b/src/global.scss index 44fd495a..79197538 100644 --- a/src/global.scss +++ b/src/global.scss @@ -124,10 +124,6 @@ ion-header { } } -.ion-content-parallax { - @include ion-content-parallax(); -} - ion-alert { button.alert-button.preferred { background-color: var(--ion-color-primary); diff --git a/src/theme/common/_ion-content-parallax.scss b/src/theme/common/_ion-content-parallax.scss index 35e35f54..c3599b8c 100644 --- a/src/theme/common/_ion-content-parallax.scss +++ b/src/theme/common/_ion-content-parallax.scss @@ -12,43 +12,34 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ +$overscroll-padding: 720px; +$parallax-strength: 2; +$default-parallax-content-size: 230px; -@mixin ion-content-parallax( - $parallax-background: var(--ion-color-primary), - $background: var(--ion-background-color), - $parallax-strength: 2, - $overscroll-padding: 720px, - $content-size: 230px -) { - &::part(background) { - background: $background; - } - &::part(scroll) { - perspective: 2px; - perspective-origin: center top; - } - > div { - transform-style: preserve-3d; - position: relative; - - &::before { - content: ' '; - position: absolute; - top: 0; - right: 0; - left: 0; - - $height: calc($content-size + $overscroll-padding); - $translateY: calc($overscroll-padding * $parallax-strength); - $translateZ: calc(-1px * $parallax-strength); - $transform-origin: calc($parallax-strength * $parallax-strength * $overscroll-padding); - - height: $height; - width: 150%; - transform-origin: 50% $transform-origin; - transform: translate3d(0px, $translateY, $translateZ) scale($parallax-strength); - - background: $parallax-background; - } - } +ion-content::part(parallax-scroll) { + perspective: 2px; + perspective-origin: center top; +} + +ion-content::part(parallax-parent) { + transform-style: preserve-3d; + position: relative; +} + +ion-content::part(parallax) { + position: absolute; + top: 0; + right: 0; + left: 0; + + $translateY: calc($overscroll-padding * $parallax-strength); + $translateZ: calc(-1px * $parallax-strength); + $transform-origin: calc($parallax-strength * $parallax-strength * $overscroll-padding); + + height: calc(var(--parallax-content-size, $default-parallax-content-size) + $overscroll-padding); + width: 150%; + transform-origin: 50% $transform-origin; + transform: translate3d(0px, $translateY, $translateZ) scale($parallax-strength); + + background: var(--parallax-background, var(--ion-color-primary)); }