Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions demo/backend/_includes/icons/back-white.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

<style
id="text"
marginHorizontal="24"
marginBottom="16"
/>

<style
id="instructions-text"
flex="1"
alignItems="flex-end"
/>

<style
id="red-square"
width="100"
height="100"
backgroundColor="#ff0000"
marginHorizontal="24"
marginVertical="32"
alignSelf="center"
/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
---
permalink: "/backend/advanced/community/scroll-transform.xml"
tags: "Advanced/Community/Elements"
hv_title: "Scroll Transform"
---

{% extends 'templates/base.xml.njk' %}

{% block styles %}
{% include './_styles.xml.njk' %}
<style id="hero-container" width="100%" position="relative" top="-24" />
<style id="hero-image" width="460" height="360" />
<style id="hero-title" fontSize="36" color="white" position="absolute" bottom="16" left="0" zIndex="100" />

<style id="header-transparent" backgroundColor="transparent" borderBottomColor="transparent" />

<style id="hero-header-container"
position="absolute"
top="0"
bottom="0"
left="0"
height="0"
width="100%"
zIndex="1000"
/>
<style id="hero-header"
backgroundColor="white"
borderBottomWidth="1"
borderBottomColor="black"
flexDirection="row"
padding="16"
alignItems="center"
position="absolute"
top="0"
paddingTop="60"
width="100%"
/>
{% endblock %}

{% block body %}

<header style="hero-header-container">

<scroll-transform:container
xmlns:scroll-transform="https://hyperview.org/scroll-transform"
>
<scroll-transform:transform
context-key="scroll-view"
style-attr="opacity"
scroll-range="[200, 230]"
attr-range="[1, 0]"
axis="vertical"
/>
<view style="hero-header header-transparent">
<view action="back" href="#" style="header-btn">
{% include 'icons/back-white.svg' %}
</view>
</view>
</scroll-transform:container>

<scroll-transform:container
xmlns:scroll-transform="https://hyperview.org/scroll-transform"
>
<scroll-transform:transform
context-key="scroll-view"
style-attr="opacity"
scroll-range="[200, 230]"
attr-range="[0, 1]"
axis="vertical"
/>
<view style="hero-header">
<view action="back" href="#" style="header-btn">
{% include 'icons/back.svg' %}
</view>
<text style="header-title" key="title">{{ hv_title }}</text>
</view>
</scroll-transform:container>

</header>

<view
style="main"
xmlns:scroll="https://hyperview.org/hyperview-scroll"
scroll="true"
scroll:context-key="scroll-view"
scroll:event-throttle="16"
>

<view style="hero-container">
<scroll-transform:container
xmlns:scroll-transform="https://hyperview.org/scroll-transform"
transform-origin="bottom center"
>
<scroll-transform:transform
context-key="scroll-view"
transform-attr="scale"
scroll-range="[0, -200]"
attr-range="[1, 1.6]"
axis="vertical"
duration="0"
/>

<image
style="hero-image"
source="https://instawork-pro-vetting-dev.imgix.net/skills_hero_images/a1c548b2-2379-4482-83d8-3bfa6d571ff1.png?w=360&h=360&dpr=2"
/>
</scroll-transform:container>

<scroll-transform:container
xmlns:scroll-transform="https://hyperview.org/scroll-transform"
>
<scroll-transform:transform
context-key="scroll-view"
style-attr="opacity"
scroll-range="[100, 180]"
attr-range="[1, 0]"
axis="vertical"
/>
<text style="header-title text hero-title">{{ hv_title }}</text>
</scroll-transform:container>
</view>

<text style="text">
Velit voluptas et alias atque provident sapiente consequuntur deserunt. Dolorem et
non error dolorem voluptate amet accusantium. Corporis rerum sed labore quae sed qui quis quasi. Illo pariatur sint qui.
Quasi quaerat id molestias. Necessitatibus et ipsa quia asperiores laborum neque. Quisquam dolorem consequatur illum. Ut
magni iusto explicabo blanditiis quasi laborum incidunt earum. Eius ut in rerum ipsam. Officiis dolores suscipit
consequatur placeat commodi eum. Vel possimus placeat aut eos tempore saepe. Esse assumenda eum illo sed aut earum quia
voluptatibus. Recusandae qui iusto corporis sed atque. Veniam et possimus praesentium. Cum molestiae non velit minus
voluptatibus quos illo sed. Et omnis ut soluta qui inventore molestias. Dolores voluptatem perspiciatis exercitationem
consectetur minus illum. Quos quaerat omnis aut eius eos dolores velit. Et quia vel ea unde eum repudiandae. Repellat et
ab sed.
</text>

<text style="text">
Velit voluptas et alias atque provident sapiente consequuntur deserunt. Dolorem et
non error dolorem voluptate amet accusantium. Corporis rerum sed labore quae sed qui quis quasi. Illo pariatur sint qui.
Quasi quaerat id molestias. Necessitatibus et ipsa quia asperiores laborum neque. Quisquam dolorem consequatur illum. Ut
magni iusto explicabo blanditiis quasi laborum incidunt earum. Eius ut in rerum ipsam. Officiis dolores suscipit
consequatur placeat commodi eum. Vel possimus placeat aut eos tempore saepe. Esse assumenda eum illo sed aut earum quia
voluptatibus. Recusandae qui iusto corporis sed atque. Veniam et possimus praesentium. Cum molestiae non velit minus
voluptatibus quos illo sed. Et omnis ut soluta qui inventore molestias. Dolores voluptatem perspiciatis exercitationem
consectetur minus illum. Quos quaerat omnis aut eius eos dolores velit. Et quia vel ea unde eum repudiandae. Repellat et
ab sed.
</text>
<text style="text">
Velit voluptas et alias atque provident sapiente consequuntur deserunt. Dolorem et
non error dolorem voluptate amet accusantium. Corporis rerum sed labore quae sed qui quis quasi. Illo pariatur sint qui.
Quasi quaerat id molestias. Necessitatibus et ipsa quia asperiores laborum neque. Quisquam dolorem consequatur illum. Ut
magni iusto explicabo blanditiis quasi laborum incidunt earum. Eius ut in rerum ipsam. Officiis dolores suscipit
consequatur placeat commodi eum. Vel possimus placeat aut eos tempore saepe. Esse assumenda eum illo sed aut earum quia
voluptatibus. Recusandae qui iusto corporis sed atque. Veniam et possimus praesentium. Cum molestiae non velit minus
voluptatibus quos illo sed. Et omnis ut soluta qui inventore molestias. Dolores voluptatem perspiciatis exercitationem
consectetur minus illum. Quos quaerat omnis aut eius eos dolores velit. Et quia vel ea unde eum repudiandae. Repellat et
ab sed.
</text>
<text style="text">
Velit voluptas et alias atque provident sapiente consequuntur deserunt. Dolorem et
non error dolorem voluptate amet accusantium. Corporis rerum sed labore quae sed qui quis quasi. Illo pariatur sint qui.
Quasi quaerat id molestias. Necessitatibus et ipsa quia asperiores laborum neque. Quisquam dolorem consequatur illum. Ut
magni iusto explicabo blanditiis quasi laborum incidunt earum. Eius ut in rerum ipsam. Officiis dolores suscipit
consequatur placeat commodi eum. Vel possimus placeat aut eos tempore saepe. Esse assumenda eum illo sed aut earum quia
voluptatibus. Recusandae qui iusto corporis sed atque. Veniam et possimus praesentium. Cum molestiae non velit minus
voluptatibus quos illo sed. Et omnis ut soluta qui inventore molestias. Dolores voluptatem perspiciatis exercitationem
consectetur minus illum. Quos quaerat omnis aut eius eos dolores velit. Et quia vel ea unde eum repudiandae. Repellat et
ab sed.
</text>
</view>
{% endblock %}
51 changes: 51 additions & 0 deletions demo/schema/scroll-transform.xsd
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"
attributeFormDefault="qualified"
elementFormDefault="qualified"
targetNamespace="https://hyperview.org/scroll-transform"
xmlns:scroll-transform="https://hyperview.org/scroll-transform"
xmlns:hv="https://hyperview.org/hyperview"
>
<xs:import
namespace="https://hyperview.org/hyperview"
schemaLocation="hyperview.xsd"
/>
<xs:simpleType name="scroll-range">
<xs:restriction base="xs:string">
<xs:pattern value="\[-?[0-9]+,\s?-?[0-9]+\]" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="attr-range">
<xs:restriction base="xs:string">
<xs:pattern value="\[-?[0-9]\.?[0-9]*,\s?-?[0-9]\.?[0-9]*\]" />
</xs:restriction>
</xs:simpleType>
<xs:element name="container">
<xs:complexType>
<xs:sequence>
<xs:element ref="scroll-transform:transform" minOccurs="0" maxOccurs="unbounded" />
<xs:any minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
<xs:attribute name="style" type="hv:styleList" form="unqualified" />
<xs:attribute name="transformOrigin" type="xs:string" form="unqualified" />
</xs:complexType>
</xs:element>
<xs:element name="transform">
<xs:complexType>
<xs:attribute name="context-key" type="xs:token" use="required" />
<xs:attribute name="style-attr" type="xs:token" />
<xs:attribute name="transform-attr" type="xs:token" />
<xs:attribute name="axis">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="horizontal" />
<xs:enumeration value="vertical" />
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="duration" type="xs:nonNegativeInteger" />
<xs:attribute name="scroll-range" type="scroll-transform:scroll-range" />
<xs:attribute name="attr-range" type="scroll-transform:attr-range" />
</xs:complexType>
</xs:element>
</xs:schema>
132 changes: 132 additions & 0 deletions demo/src/Components/ScrollTransform/ScrollTransform.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import React, { useEffect, useRef } from 'react';
import {
calculateValue,
namespaceURI,
parseTransformElement,
type TransformConfig,
} from './utils';
import { createStyleProp, useScrollContext } from 'hyperview';
import * as Render from 'hyperview/src/services/render';
import { Animated } from 'react-native';
import type { HvComponentProps } from 'hyperview';
import { NODE_TYPE } from 'hyperview';


interface AnimatedValues {
[key: string]: Animated.Value;
}

const ScrollTransformContainer = (props: HvComponentProps) => {
const style = createStyleProp(props.element, props.stylesheets, {
...props.options,
styleAttr: 'style',
});

// Parse transformOrigin prop
const transformOriginValue = props.element.getAttribute('transform-origin');

// Parse transform elements from children
const transformConfigs: TransformConfig[] = [];
const transformElements = Array.from(props.element.childNodes).filter(
(child) =>
child.nodeType === NODE_TYPE.ELEMENT_NODE &&
(child as Element).namespaceURI === namespaceURI &&
(child as Element).localName === 'transform'
);

transformElements.forEach((element) => {
try {
const config = parseTransformElement(element as Element);
transformConfigs.push(config);
} catch (error) {
console.error('Error parsing transform element:', error);
}
});

// Create animated values for each transform
const animatedValuesRef = useRef<AnimatedValues>({});
const { offsets } = useScrollContext();
//console.log('offsets', offsets);
// Initialize animated values
transformConfigs.forEach((config, index) => {
const key = `${config.styleAttr || config.transformAttr}-${index}`;
if (!animatedValuesRef.current[key]) {
// Assumes the default scroll position is 0
const defaultPosition = { x: 0, y: 0 };
const contextPosition = offsets[config.contextKey] || defaultPosition;
const position = config.axis === 'horizontal' ? contextPosition.x : contextPosition.y;
const initialValue = calculateValue(position, config.scrollRange, config.attrRange);
animatedValuesRef.current[key] = new Animated.Value(initialValue);
}
});

// Update animated values when scroll position changes
useEffect(() => {
transformConfigs.forEach((config, index) => {
const key = `${config.styleAttr || config.transformAttr}-${index}`;
const defaultPosition = { x: 0, y: 0 };
const contextPosition = offsets[config.contextKey] || defaultPosition;
const position = config.axis === 'horizontal' ? contextPosition.x : contextPosition.y;
const toValue = calculateValue(position, config.scrollRange, config.attrRange);

Animated.timing(animatedValuesRef.current[key], {
duration: config.duration,
toValue,
useNativeDriver: true,
}).start();
});
}, [transformConfigs, offsets]);

// Build animated style from transform configs
const animatedStyle: any = {};
const transformArray: any[] = [];

transformConfigs.forEach((config, index) => {
const key = `${config.styleAttr || config.transformAttr}-${index}`;
const animatedValue = animatedValuesRef.current[key];

if (config.styleAttr) {
// For style attributes like opacity, backgroundColor, etc.
animatedStyle[config.styleAttr] = animatedValue;
} else if (config.transformAttr) {
// For transform attributes like scale, rotate, etc.
const transformObj: any = {};
transformObj[config.transformAttr] = animatedValue;
transformArray.push(transformObj);
}
});

if (transformArray.length > 0) {
animatedStyle.transform = transformArray;
}

// Apply transformOrigin if specified
if (transformOriginValue) {
animatedStyle.transformOrigin = transformOriginValue;
}

// Filter out transform child elements since they're configuration, not content
const nonTransformChildNodes = Array.from(props.element.childNodes).filter(
(child) => {
if (child.nodeType === NODE_TYPE.ELEMENT_NODE) { // Element node
const element = child as Element;
return !(element.namespaceURI === namespaceURI && element.localName === 'transform');
}
return true; // Keep text nodes and other types
}
);

const children = Render.renderChildNodes(
nonTransformChildNodes,
props.stylesheets,
props.onUpdate,
props.options,
);

return <Animated.View style={[style, animatedStyle]}>{children}</Animated.View>;
};

ScrollTransformContainer.namespaceURI = namespaceURI;
ScrollTransformContainer.localName = 'container';

export { ScrollTransformContainer };
1 change: 1 addition & 0 deletions demo/src/Components/ScrollTransform/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ScrollTransformContainer } from './ScrollTransform';
Loading