react-native-masked-view / masked-view

React Native Masked View Library
MIT License
975 stars 126 forks source link

[HELP] Trying to use Flatlist items as a mask for a linear gradient. #57

Open wzahedi opened 4 years ago

wzahedi commented 4 years ago

I am trying to mask Flatlist elements using linear gradient as shown in the image below. The text on the "message bubbles" are not rendering. I was able to make the flatlist scrollable by going into MaskedView.js and making pointerEvents to "auto". I am using the Flatlist as the maskElement, and a LinearGradient (expo-linear-gradient) as the child. Let me know of any ideas please. Another option I am considering is using gl-react for this, however I want to see if I can simply fix the text not showing up using this library. Thanks!

image

j-rayhan commented 4 years ago

Same problem Doesn't work with Flatlist item

import React from 'react';
import { View, StyleSheet, Dimensions, FlatList, Image, Animated } from 'react-native';
import MaskedView from '@react-native-community/masked-view';
import Svg, { Rect } from 'react-native-svg';
import { LinearGradient } from 'expo-linear-gradient';
//
const { width, height } = Dimensions.get('window')
const ITEM_SIZE = width * 0.72
const BACKDROP_HEIGHT = height * 0.6
const AnimatedSvg = Animated.createAnimatedComponent(Svg)

const Backdrop = ({ movies, scrollX }) => {
  return (
    <View style={styles.backdrop}>
      <FlatList
        data={movies}
        keyExtractor={item => item.key}
        renderItem={({ item, index }) => {

          if (!item.backdrop) {
            return
          }

          const inputRange = [
            (index - 2) * ITEM_SIZE, 
            (index - 1) * ITEM_SIZE
          ]

          const translateX = scrollX.interpolate({
            inputRange,
            outputRange: [-width, 0]
          })

          return (
            <MaskedView
              style={StyleSheet.absoluteFill}
              maskElement={
                <AnimatedSvg
                  width={width}
                  height={height}
                  viewBox={`0 0 ${width} ${height}`}
                  style={{ transform: [{ translateY: translateX }]}}
                >
                  <Rect 
                    x="0"
                    y="0"
                    width={width}
                    height={height}
                    fill="red"
                  />
                </AnimatedSvg>
              }
            >
              <Image
                source={{ uri: item.backdrop }}
                style={{
                  width,
                  height: 100,
                  resizeMode: 'cover'
                }}
              />
            </MaskedView>
          )
        }}
      />
      <LinearGradient 
        colors= {[ 'transparent', 'red']}
        style={{
          width,
          height: BACKDROP_HEIGHT,
          position: 'absolute',
          bottom: 0
        }}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  backdrop: {
    position: 'absolute',
    top: 0,
    width: width,
    height: BACKDROP_HEIGHT,
  },
})
export default Backdrop;
Onibenjo commented 4 years ago

Any updates on this?

Onibenjo commented 4 years ago

@j-rayhan For android,

Best way is use Animated.View

import React from "react";
import {
  Dimensions,
  FlatList,
  Image,
  StyleSheet,
  View,
  Animated,
} from "react-native";
import { LinearGradient } from "expo-linear-gradient";
import { BACKDROP_HEIGHT, ITEM_SIZE } from "../constants";

const { width, height } = Dimensions.get("window");

const BackDrop = ({ scrollX, movies }) => {
  return (
    <View style={{ height: BACKDROP_HEIGHT, width, position: "absolute" }}>
      <FlatList
        data={movies}
        keyExtractor={(item) => item.key + "-backdrop"}
        removeClippedSubviews={false}
        contentContainerStyle={{ width, height: BACKDROP_HEIGHT }}
        renderItem={({ item, index }) => {
          if (!item.backdrop) {
            return null;
          }
          const translateX = scrollX.interpolate({
            inputRange: [(index - 2) * ITEM_SIZE, (index - 1) * ITEM_SIZE],
            outputRange: [0, width],
            // extrapolate:'clamp'
          });
          return (
            <Animated.View
              removeClippedSubviews={false}
              style={{
                position: "absolute",
                width: translateX,
                height,
                overflow: "hidden",
              }}>
              <Image
                source={{ uri: item.backdrop }}
                style={{
                  width,
                  height: BACKDROP_HEIGHT,
                  position: "absolute",
                }}
              />
            </Animated.View>
          );
        }}
      />
      <LinearGradient
        colors={["rgba(0, 0, 0, 0)", "white"]}
        style={{
          height: BACKDROP_HEIGHT,
          width,
          position: "absolute",
          bottom: 0,
        }}
      />
    </View>
  );
};

export default BackDrop;

const styles = StyleSheet.create({
  container: {
    position: "absolute",
    width,
    height: BACKDROP_HEIGHT,
  },
  backdropImage: {
    width,
    height: BACKDROP_HEIGHT,
    resizeMode: "cover",
  },
  absolute: { position: "absolute" },
  gradient: {
    width: width,
    height: BACKDROP_HEIGHT,
    position: "absolute",
    bottom: 0,
  },
});

and make sure u set useNativeDriver to false

<Animated.FlatList
        showsHorizontalScrollIndicator={false}
        data={data}
        keyExtractor={(item) => item.key}
        horizontal
        contentContainerStyle={{
          alignItems: "center",
        }}
        snapToInterval={ITEM_SIZE} // maes the slide snap to one per scroll
        decelerationRate={Platform.OS === "ios" ? 0 : 0.85} // makes the snap efective
        renderToHardwareTextureAndroid
        snapToAlignment="start"
        bounces={false} // removes bounce on first slide
        onScroll={Animated.event(
          [{ nativeEvent: { contentOffset: { x: scrollX } } }],
          { useNativeDriver: Platform.OS === "ios" }
        )} // making active bigger on scroll
        scrollEventThrottle={16} 

        //  .............................................. rest of code