Giter Site home page Giter Site logo

alantoa / react-native-awesome-slider Goto Github PK

View Code? Open in Web Editor NEW
215.0 2.0 25.0 14.31 MB

πŸš€ An anwesome <Slider/> that supports various features haptic, lottie, animation, ballon, etc.

License: MIT License

JavaScript 6.27% TypeScript 93.73%
react-native-component react-native-slider react-native-scrubber react-native-reanimated react-native-gesture-handler react-native

react-native-awesome-slider's Introduction

React Native Awesome Slider

Reanimated v2 version npm npm runs with expo

An awesome React Native Slider component powered by Reanimated v2 and react-native-gesture-handler.

Installation

First you have to follow installation instructions of Reanimated v2 and react-native-gesture-handler

If you react-native-gesture-handler version >= 2:

yarn add react-native-awesome-slider

else:

yarn add react-native-awesome-slider@1

Example usage

Basic use:

import { useSharedValue } from 'react-native-reanimated';
import { Slider } from 'react-native-awesome-slider';

export const Example = () => {
  const progress = useSharedValue(30);
  const min = useSharedValue(0);
  const max = useSharedValue(100);
  return (
    <Slider
      style={styles.container}
      progress={progress}
      minimumValue={min}
      maximumValue={max}
    />
  );
};

Change slider theme color?

Use the Theme object.

<Slider
  theme={{
    disableMinTrackTintColor: '#fff',
    maximumTrackTintColor: '#fff',
    minimumTrackTintColor: '#000',
    cacheTrackTintColor: '#333',
    bubbleBackgroundColor: '#666',
    heartbeatColor: '#999',
  }}
/>

For more usage, please view Example.

Add pan haptic feedback?

  1. You need add haptics feedback lib to you project.
  1. Add onHapticFeedback callback to you slider component.
<Slider
  onHapticFeedback={() => {
    ReactNativeHapticFeedback.trigger('impactLight', {
      enableVibrateFallback: true,
      ignoreAndroidSystemSettings: false,
    });
  }}
/>
  1. Set haptic mode, if you open 'step' prop, you need set hapticMode=HapticModeEnum.STEP, otherwise set to hapticMode=HapticModeEnum.BOTH.

  2. βœ… Finish!

Why use this library?

  • Pure javascript slider implementations usually rely on react-native's gesture events which may inadvertently trigger 'swipe to go back' events as you pan the slider. ❌
  • Native sliders rely on state updates, which can cause performance issues. ❌

react-native-awesome-slider relies on reanimated's ShareValue ability to run code directly in the UIThread to enhance performance, and react-native-gesture-handle won't interfere with your swiping gestures. ✨

Features

  • 100% written inΒ TypeScript.
  • 100% built uponΒ react-native-reanimated and react-native-gesture-handler.
  • Supports Tap & Pan triggering.
  • Support for a discrete slider.
  • Support haptic feedback.
  • and more...

TODO list

  • Support step props
  • Optimize bubble & thumb
  • Rewrite using react-native-gesture-handler v2
  • Support marks props
  • Web Support

Configuration

TheΒ <Slider/>Β component has the following configuration properties:

Name Type Description Required Default Value
theme object The slider theme color ❌ { // Color to fill the progress in the seekbar minimumTrackTintColor: string, // Color to fill the background in the seekbar maximumTrackTintColor: string, // Color to fill the cache in the seekbar cacheTrackTintColor: string, // Color to fill the bubble backgrouundColor disableMinTrackTintColor: string, // Disabled color to fill the progress in the seekbar bubbleBackgroundColor: string // Color to fill the heartbeat animation in the seekbar heartbeatColor: string }
style ViewStyle ❌
borderColor string Color of the border of the slider, also you can use containerStyle . ❌ transparent
bubble (number) => string Get the current value of the slider as you slide it, and returns a string to be used inside the bubble. ❌ (number) => string
progress Animated.SharedValue<number> Current value of the slider βœ… 0
cache Animated.SharedValue<number> Cache value of the slider ❌ 0
minimumValue Animated.SharedValue<number> An Animated.SharedValue from react-native-reanimated library which is the minimum value of the slider. βœ… undefined
maximumValue Animated.SharedValue<number> An Animated.SharedValue from react-native-reanimated library which is the maximum value of the slider. βœ… undefined
onSlidingStart () => void Callback called when the sliding interaction starts ❌ undefined
onValueChange (number) => void Callback called when the slider value changes ❌ undefined
onSlidingComplete (number) => void Callback called when the sliding interaction stops. The updated slider value will be passed as argument ❌ undefined
renderBubble () => React.ReactNode A custom bubble component that will be rendered while sliding. ❌ See the <Bubble/> component
setBubbleText (string) => void This function will be called while sliding and can be used to update the text in a custom bubble component. ❌ current slider value
bubbleTranslateY number Value to pass to the container of the bubble as translateY ❌ 7
renderThumb () => React.ReactNode Render custom thumb image. If you need to customize thumb, you also need to set the thumb width ❌ ReactNode
renderMark ({ index }: { index: number }) => React.ReactNode Render custom mark element. If you need to customize mark, you also need to set the mark width ❌ ReactNode
thumbWidth number Thumb elements width ❌ 15
disable boolean Disable user interaction with the slider ❌ false
disableTapEvent boolean Enable tap event change value. Defaults to `true` ❌ true
bubbleMaxWidth number The maximum width of the bubble component ❌ 100
bubbleTextStyle TextStyle Bubble text style ❌
bubbleContainerStyle ViewStyle Bubble container text style ❌
isScrubbing Animated.SharedValue<boolean> callback slider is scrubbing status ❌ undefined
onTap () => void A callback for when the slider is tapped.(Useful for video-player scrubbing.) ❌ undefined
thumbScaleValue Animated.SharedValue<number> Control thumb’s transform-scale animation. ❌ undefined
sliderHeight number The height of the slider component ❌ 30
containerStyle ViewStyle styles to be applied to the slider container component ❌ { width: '100%', height: 5, borderRadius: 2, borderColor: borderColor, overflow: 'hidden', borderWidth: 1, backgroundColor: maximumTrackTintColor, },
panHitSlop object pan gesture hit slop ❌ `{ top: 8, left: 0, bottom: 8, right: 0,} `
step number Step value of the slider. The value should be between 0 and maximumValue - minimumValue) ❌ undefined
snapToStep boolean Enables or disables step snapping ❌ true
markWidth number Step mark width, if you need custom mark style, you must be fix this width ❌ 4
marksStyle ViewStyle Step mark style ❌ {width: {markWidth}, height: 4, backgroundColor: '#fff', position: 'absolute', top: 2}
onHapticFeedback function Haptic feedback callback ❌ undefined
hapticMode enum haptic feedback mode ❌ HapticModeEnum.NONE
panDirectionValue enum Current swipe direction ❌ undefined
disableTrackFollow boolean Disable track follow thumb.(Commonly used in video audio players) ❌ false
bubbleWidth number Bubble width, If you set this value, bubble positioning left & right will be clamp. ❌ 0
activeOffsetX number | number[] Range along X axis (in points) where fingers travels without activation of gesture. Moving outside of this range implies activation of gesture. ❌ undefined
activeOffsetY number | number[] Range along Y axis (in points) where fingers travels without activation of gesture. Moving outside of this range implies activation of gesture. ❌ undefined
failOffsetX number | number[] When the finger moves outside this range (in points) along X axis and gesture hasn't yet activated it will fail recognizing the gesture. Range can be given as an array or a single number ❌ undefined
failOffsetY number | number[] When the finger moves outside this range (in points) along Y axis and gesture hasn't yet activated it will fail recognizing the gesture. Range can be given as an array or a single number ❌ undefined
heartbeat boolean When 'heartbeat' is set to true, the progress bar color will animate back and forth between its current color and the color specified for the heartbeat. useful for loading state ❌ undefined

react-native-awesome-slider's People

Contributors

alantoa avatar alirezahadjar avatar bayramito avatar chijun950314 avatar computerjazz avatar efstathiosntonas avatar jacobmolby avatar jay-jlm avatar joehoel avatar julienblc avatar layerok avatar mikeislearning avatar mohammadalijf avatar owaiswiz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

react-native-awesome-slider's Issues

Slider not updated when sharedValue.value is updated

Hello !

I have something weird in a project. I have a slider, with answers just upon it. When I change the value of the slider, the answers's style is well updated. But when I press an answer, which call the update of the shareValue.value, the slider si not updated.

I've created a repo if you want to check on your computer : https://github.com/Julienblc/test-slider

Here is a video to show the behaviour :

test-slider.mov

What have I done wrong ?

Wrong progress is displayed after minimumValue/maximumValue is changed

You can see the bug in the video below.

Initially min.value is 0, and max.value is 100
I added 2 buttons.
One button sets min.value = 50, and another does max.value = 50.
After I click on one of these buttons, progress value will start to count incorrectly

bug-when-changing-min-max-values.mp4

The thumb doesn't follow when the `progress` value has actually changed

Hi guys, I am using react-native-awesome-slider 2.4.0 version but I encounter some strange things. Here is my code & a reference video. What should I modify in the code? Thank u very much!

  1. the thumb doesn't follow while the progress has actually changed
  2. the bubble & the mark seems to have no effect
import {Box} from 'native-base'

const budget = 30 // it is a value from redux store
const progress = useSharedValue(budget);
const minAnimatedValue = useSharedValue(min);
const maxAnimatedValue = useSharedValue(max);

<>
                 <Slider
                        renderBubble={() => <Flex color={"green"}>{budget}</Flex>}
                        renderMark={() => <Text style={{ width: "100%", color: "green" }} >{budget}</Text>}
                        bubble={(s: number) => (Math.floor(Number(s))).toString()}
                        theme={{
                            disableMinTrackTintColor: '#fff',
                            maximumTrackTintColor: '#fff',
                            minimumTrackTintColor: '#999',
                            cacheTrackTintColor: '#333',
                            bubbleBackgroundColor: '#666',
                        }}
                        renderThumb={() => <Box backgroundColor={"white"} w={4} h={4} borderWidth={4} borderRadius={32} borderColor={"#999"} />}
                        minimumValue={minAnimatedValue}
                        maximumValue={maxAnimatedValue}
                        thumbScaleValue={thumbScaleValue}
                        onValueChange={(num) => setBudget(Math.floor(num))}
                        progress={/* { value: budget } */ progress}
                    />
</>


RPReplay_Final1687283982.MP4

after layout, changing step, minimum, maximum value does not has effect on component

Most of calculations are happening onLayout handler in component. after layout if we change step, minimumValue, maximumValue component would not show correct values and behaviours.

one work around is to set keys with relative props so it would force to trigger onLayout.

<Slider
      /**
       * This key is needed here though it looks useless.
       * slider library calculate values on layout...
       * to enforce the slider to calculate new values we need to pass this key so it would update itself when step or maximumValue or minimumValue changes
       */
      key={`${step}${wrapperMaximumValue}${wrapperMinimumValue}`}
      ... />

NativeViewHierarchyManager: Unable to update properties for view tag error thrown in logcat android

Current behaviour
When using Slider and i slide it many times, the below error is continuously occurring in Logcat when run on an Android device.
E/unknown:NativeViewHierarchyManager: Unable to update properties for view tag 3083
com.facebook.react.uimanager.IllegalViewOperationException: ViewManager for tag 3083 could not be found.

    at com.facebook.react.uimanager.NativeViewHierarchyManager.resolveViewManager(NativeViewHierarchyManager.java:109)
    at com.facebook.react.uimanager.NativeViewHierarchyManager.updateProperties(NativeViewHierarchyManager.java:133)
    at com.facebook.react.uimanager.UIImplementation.synchronouslyUpdateViewOnUIThread(UIImplementation.java:291)
    at com.swmansion.reanimated.NodesManager.updateProps(NodesManager.java:557)
    at com.swmansion.reanimated.NativeProxy.updateProps(NativeProxy.java:93)
    at com.swmansion.reanimated.NativeProxy$AnimationFrameCallback.onAnimationFrame(Native Method)
    at com.swmansion.reanimated.NodesManager.onAnimationFrame(NodesManager.java:252)
    at com.swmansion.reanimated.NodesManager.access$000(NodesManager.java:65)
    at com.swmansion.reanimated.NodesManager$1.doFrameGuarded(NodesManager.java:153)
    at com.facebook.react.uimanager.GuardedFrameCallback.doFrame(GuardedFrameCallback.java:29)
    at com.facebook.react.modules.core.ReactChoreographer$ReactChoreographerDispatcher.doFrame(ReactChoreographer.java:175)
    at com.facebook.react.modules.core.ChoreographerCompat$FrameCallback$1.doFrame(ChoreographerCompat.java:85)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1299)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1309)
    at android.view.Choreographer.doCallbacks(Choreographer.java:923)
    at android.view.Choreographer.doFrame(Choreographer.java:847)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1283)
    at android.os.Handler.handleCallback(Handler.java:942)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loopOnce(Looper.java:226)
    at android.os.Looper.loop(Looper.java:313)
    at android.app.ActivityThread.main(ActivityThread.java:8741)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)

Screenshots (if applicable)
error

my code

const renderSubItem = () => {
if (isCollapse) {
return null;
} else {
return (

<Image
source={imageScoring()!}
resizeMode={'stretch'}
style={styles.subItemImage}
/>

{props.scoreLabel.map((item, index) => {
return (
<Text
key={index}
style={[
styles.subItemScoreTitle,
index > 0 && styles.subItemScoreTitle2,
]}>
{item}

);
})}

{props.readonlySlider ? (

<Image
source={imageRectangle.imageSource}
resizeMode={'stretch'}
style={styles.rectangleImage}
/>
<View
style={[
styles.rectangleImageScoreContainer,
{
marginLeft:
(isNaN(score) ? 1 : score) * pxToPercentage(75) -
pxToPercentage(15),
},
]}>

{isNaN(score) ? 'NaN' : score?.toFixed(1)}



) : (
<View
style={{
marginHorizontal: pxToPercentage(30),
width: pxToPercentage(310),
}}>
<Slider
style={{ marginTop: pxToPercentage(8) }}
theme={{
minimumTrackTintColor:
themes['App Theme']['slider_track_color-1'],
maximumTrackTintColor:
themes['App Theme']['slider_track_color-2'],
bubbleBackgroundColor: '#fff',
bubbleTextColor: themes['App Theme']['slider_track_color-1'],
}}
renderBubble={() => {
//hidden tooltip
return ;
}}
containerStyle={styles.slider}
progress={{
value: score,
}}
minimumValue={{
value: 0,
}}
maximumValue={{
value: 4,
}}
thumbWidth={24}
onValueChange={(value: any) => {
const newValue = typeof value === 'number' ? value : value[0];
// const scoreChange = (newValue / 100) * scoreSuffixes;
if (newValue !== score) {
setScore(newValue);
props.onSliderValueChange?.(getItemResult(newValue));
}
}}
bubble={(number: any) => {
return number.toFixed(1);
}}
/>

)}

);
}
};

  • this using
    <>
    <TouchableOpacityRN
    activeOpacity={0.75}
    onPress={() => setCollapse(!isCollapse)}
    style={[styles.itemContainer, !isCollapse && styles.itemContainer2]}>
    <InfoTooltipComponent
    popoverComponent={{I18n.t(props.model?.tooltip)}}>

    {I18n.t(props.model?.fullName)}

      <View style={styles.scoreContainerStyle}>
        <Text style={[styles.scoreText]}>
          {toFixed(score)}
          {props.model?.scoreSuffixes}
        </Text>
    
        {onRenderArrowIcon(
          isCollapse,
          props.model?.key === props.scoring?.values?.pasi?.key,
        )}
      </View>
    </TouchableOpacityRN>
    {renderSubItem()}
    

    </>

Slider don't show progress when is disable

Hello, first of all, thank you for your work on this, is amazing!

I have an issue, when I add the prop disable the slider does not show the current progress, Am I missing something?

import { Slider } from 'react-native-awesome-slider';

<Slider
    disable={true}
    minimumValue={min}
    maximumValue={max}
    progress={progress}
    thumbWidth={0}
    renderBubble={() => null}
    heartbeat={bufferingDuringPlay}
 />

How to set initial step?

I would like to have the slider at a given starting step, when you see the slider intially.
So let's say i have a slider with 4 steps, when the slider is in view for the first time, on initial render, i would like it to be at step 2.
Now every time the slider in rendered, it starts at 0.

I thought the progress prop is responsible for this, but it always stays at 0. Even if i set arbitrary numbers to it.

This is how my values are defined:

 const progress = useSharedValue(volumeLevel);
 const min = useSharedValue(1);
 const max = useSharedValue(5);

and the Slider component:

        <Slider
          step={4}
          progress={progress}
          minimumValue={min}
          maximumValue={max}
          sliderHeight={4}
          thumbWidth={18}
          bubbleMaxWidth={58}
          renderMark={renderMark}
          renderBubble={renderBubble}
          onSlidingComplete={onSlidingComplete}
          onValueChange={onValueChange}
          style={styles.slider}
          theme={theme}
        />

How should i set an initial step position where the slider will be?

I looked into the source code and i managed to do this by passing a value to const thumbIndex = useSharedValue(0);, instead of 0, but this way the slider is at 0 for a few milliseconds, then it snaps to the desired step. And this can be frustrating to the user.

What am i doing wrong?

Only allow drag input if user is holding onto the thumb

Right now, I can press anywhere along the slider. The thumb will move to where I pressed and follow as I drag along the slider.

I want to only allow dragging if the user presses on the thumb first, before dragging. Is this possible to achieve?

NPM peerdependency error

Hi, I'm getting the following error when I try to npm install

npm ERR! Found: [email protected]
npm ERR! node_modules/react-native-reanimated
npm ERR!   react-native-reanimated@"^2.4.1" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer react-native-reanimated@">2" from [email protected]
npm ERR! node_modules/react-native-awesome-slider
npm ERR!   react-native-awesome-slider@"*" from the root project

Looks like the peerd dependency in package.json isn't compatible with the latest [email protected]?

I saw other packages do the following, maybe it can help:

  "peerDependencies": {
    "react-native-reanimated": ">=2.0.0"
  },

PS thanks for the slider can't wait to try it!

Thumb "drag point" is not centered on thumb

The dragging point on the thumb seems to be located on the very left point of the thumb. This causes a "jump" when pressing the center of the thumb to drag it. An even bigger "jump" is seen when pressing on the right side of the thumb when starting to drag it.

Only way to get a smooth start of dragging is to press on the very left side of the thumb.

I am using a custom thumb, and I have set the thumb width prop to get it to fit within the track.
I can add a negative margin equal to the half of the width on my custom thumb to make the dragging work as it should (centered), but that will cause it to overflow on the left side of the track, and not reach the end of the right side of the track.

Video of the issue. First I am dragging the thumb by clicking on the right side. Then I am dragging it by clicking on the left side. As you can see, the mouse pointer is fixed to the left side of the thumb, but it should be fixed to the center.

Screen.Recording.2023-11-27.at.17.04.09.mov

How to hide bubble?

Do we have any props that can help to hide Bubble if we don't want to show it while user is panning the slider?

onStartSliding

image

Expected Behavior

Declaring onSlidingStart should not be required.

Current Behavior

Typescript complains of missing property if onSlidingStart is left undeclared.

Progress shows incorrect value when Step is set

When ever I have a step assigned to the slider the progress does not correctly display on the slider as shown.
The top number is the progress.value set at initialisation and the sliders have two different slider step counts 4 and 5 respectively.

Screenshot 2023-12-05 at 1 08 45β€―pm

I am using reanimation V3.5.4 and the latest version of react-native-awesome-slider; V2.5.4

Vertical

Hi can you add a vertical prop to make a vertical slider?

Slider thumb jumps on initial render.

https://github.com/alantoa/react-native-awesome-slider/blob/main/src/slide.tsx#L301

clamp is called with

  • value is progressToValue(progress.value) (assume it's 0),
  • lowerBound is 0,
  • upperBound is width.value - thumbWidth.

Assume thumbWidth is the default (e.g 14). width.value is updated in the onLayout callback. So it's 0 from the initial render until onLayout is called.

Thus clamp() will be called with clamp(0,0, 0 - 14) => clamp(0,0,-14), which returns -14.
So the thumb initially uses translateX: -14 and renders outside the left edge bound for the first few renders until width.value becomes available

Easiest/safe solution here would be to just change the third argument of the clamp fn from width.value - thumbWidth to Math.max(0, width.value - thumbWidth) or width.value ? width.value - thumbWidth : 0

Or alternatively return lower bound if lowerBound > upperBound in the clamp function, however I am not sure if that'll break other things which rely on the opposite behavior.

I don't notice it on my iPhone but can notice on my Android phone.


BTW This is a pretty great library. Thanks for your work on this! πŸ™πŸΌ

Gap between track and thumb with disableTrackFollow prop while seeking ends

Hi @alantoa, thanks for this awesome library.

When I use disableTrackFollow there's a gap between the track and the thumb after seeking is complete on both iOS and Android. No expensive calculations or re-renders are taking place, only the parent which holds the player controls. I'm passing the progress directly to the slider from parent.

See attached video, I've made the thumb transparent for testing purposes:

Screen.Recording.2022-06-17.at.14.46.39.mov

Parent:

  const progress = useSharedValue(0);

const playAudio = useCallback(async () => {
    await startPlayer(currentMessage?.audio?.url, (res: any) => {
      const { status } = res;
      switch (status) {
        case AUDIO_STATUS.begin: {
          setPlaying(true);
          break;
        }
        case AUDIO_STATUS.play: {
          const { currentPosition } = res.data;
          progress.value = currentPosition;  <---- setting progress here
          setPlayTime(msToHMS(currentPosition)); <-- setting current position to display mm:ss
          break;
        }
  .....
}, [])

return (
  <TrackPlayerProgress
              disabled={!playing}
              duration={currentMessage?.audio?.secs}
              messagePosition={position}
              onSeekComplete={seekPlayer}
              progress={progress}
            />
)

and the TrackPlayerProgress:

interface IProps {
  disabled: boolean;
  duration: number | undefined;
  messagePosition: string;
  onSeekComplete: (position: number) => Promise<void>;
  progress: Animated.SharedValue<number>;
  themeContext: any;
}

const TrackPlayerProgress = ({
  disabled,
  duration,
  messagePosition,
  progress,
  themeContext
}: IProps) => {
  const isScrubbing = useSharedValue(false);
  const minValue = useSharedValue(0);

  const maxValue = useDerivedValue(() => {
    return duration;
  }, [duration]);

  const pauseAudio = useCallback(async () => {
    breadcrumb("User Paused Audio Message");
    await pausePlayer();
  }, []);

  const onSlidingStart = useCallback(async () => {
    await pauseAudio();
  }, [pauseAudio]);

  const onSlidingComplete = (slideValue: number) => {
    seekPlayer(slideValue).then(() => {
      isScrubbing.value = false;
    });
  };

  const theme = useMemo(
    () => ({
      minimumTrackTintColor:
        messagePosition === "right"
          ? themeContext.chatScreen.audioPlayer.right.minimumTrack
          : themeContext.chatScreen.audioPlayer.left.minimumTrack,
      maximumTrackTintColor:
        messagePosition === "right"
          ? themeContext.chatScreen.audioPlayer.right.track
          : themeContext.chatScreen.audioPlayer.left.track,
      bubbleBackgroundColor: themeContext.chatScreen.audioPlayer.timeBubble,
      bubbleTextColor: themeContext.chatScreen.audioPlayer.timeBubbleText
    }),
    [messagePosition, themeContext]
  );

  console.count("rendering track component");

  return (
    <View style={styles.container}>
      <View style={styles.sliderInnerContainer}>
        <Slider
          bubble={msToHMS}
          disableTrackFollow
          bubbleTranslateY={Platform.OS === "ios" ? -20 : -30}
          containerStyle={styles.sliderContainer}
          disable={disabled}
          isScrubbing={isScrubbing}
          // @ts-ignore
          maximumValue={maxValue}
          minimumValue={minValue}
          onSlidingComplete={onSlidingComplete}
          onSlidingStart={onSlidingStart}
          progress={progress}
          sliderHeight={5}
          style={styles.slider}
          thumbWidth={20}
          theme={theme}
        />
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    alignItems: "center",
    marginLeft: 5,
    marginRight: 15
  },
  sliderInnerContainer: {
    maxWidth: 500,
    position: "relative",
    width: "100%"
  },
  sliderContainer: {
    borderRadius: 50
  },
  slider: {
    borderRadius: 12,
    marginBottom: 14,
    marginTop: 16
  }
});

export default React.memo(TrackPlayerProgress, isEqual);

I believe there's a miscalculation between the thumbSize and the min track position/width after the seek. Tried to debug it a bit and what fixes it somehow is to remove - thumbWidth from this function:

  const progressToValue = (value: number) => {
    'worklet';
    if (sliderTotalValue() === 0) {
      return 0;
    }
    return (value / sliderTotalValue()) * (width.value - thumbWidth);
  };

After removing the - thumbWidth it works fine but the thumb reaches the end and the audio is still playing.

Not working with reanimated v2.13.0

Description:
Upgraded from reanimated 2.8.0 to 2.13.0 slider no longer works. Thumb stopped moving and the slider stays at the initial value. Bubble still visible and moves with the gesture, so I am guessing its a compatibility issues with the new reanimated version.

Audio Player Example

Hi, so I was wondering what the best approach will be for hooking the slider up to an audio player (ps I'm using react-native-track-player).

So CurrentPosition prop updates every 100ms (useProgress hook from track player) and while I'm scrubbing I don't want the slider to update based on CurrentPosition anymore so I'm using the progress constant then.

Current Problem: Everything works but when scrubbing is done the slider jumps to prev position and then to new scrub position (Probably because currentposition is still based on prev value and don't update on time)

interface Props {
  duration: number;
  currentPosition: number;
  onSeek: (position: number) => void;
  onSeekComplete: (position: number) => void;
  onSeekStart: () => void;
}
function SeekBar(props: Props) {
  const { duration, currentPosition, onSeek, onSeekComplete, onSeekStart } = props;

  const { colors: Colors } = useTheme();

  const progress = useSharedValue(0.1);
  const min = useSharedValue(0);
  const max = useDerivedValue(() => {
    return duration;
  }, [duration]);

  const isScrubbing = useSharedValue(false);
  const prevProgress = useRef(0);

  const onSlidingComplete = (e: number) => {
    onSeekComplete(e);
    isScrubbing.value = false;
  };
  const onSlidingStart = () => {
    Haptics.selectionAsync();
    onSeekStart();
    prevProgress.current = currentPosition;
  };

  useEffect(() => {
    if (!isScrubbing.value) progress.value = currentPosition;
    if (isScrubbing.value) onSeek(progress.value);
  }, [currentPosition, isScrubbing.value, progress]);

  return (
    <View style={[styles.container]}>
      <View style={[styles.progressContainer]}>
        <Slider
          maximumValue={max}
          minimumValue={min}
          progress={progress}
          onSlidingComplete={onSlidingComplete}
          onSlidingStart={onSlidingStart}
          isScrubbing={isScrubbing}
          sliderHeight={4}
          thumbWidth={15}
          theme={{
            disableMinTrackTintColor: '#fff',
            maximumTrackTintColor: Colors.placeholderText,
            minimumTrackTintColor: Colors.blue,
            bubbleBackgroundColor: '#666',
          }}
          bubble={e => formatTime(e)}
          style={styles.slider}
          containerStyle={{ borderRadius: 2 }}
        />
      </View>
      <Text
        style={[styles.timeLabel, { marginLeft: width * 0.025, color: Colors.text }]}
        numberOfLines={1}
      >
        {formatTime(duration - currentPosition)}
      </Text>
    </View>
  );
}

export default SeekBar;

Sorry you can close this if you want to, was just curious as to what you think would be the best approach

How to update current value?

Probably I am missing something, but how to update current value from the outside? (e.g. if value comes from props).
Updating progress.value doesn't have any effect.

import { useSharedValue } from 'react-native-reanimated';
import { Slider } from 'react-native-awesome-slider';

export const Slider = ({
  value,
  onChange,
}: {
  value: number;
  onChange: Function;
}) => {
  const theme = useTheme();
  const progress = useSharedValue(value);
  const min = useSharedValue(0);
  const max = useSharedValue(10);

  useEffect(() => {
    progress.value = value;
  }, [value]);

  return (
    <Slider
      style={styles.container}
      progress={progress}
      minimumValue={min}
      maximumValue={max}
    />
  );
};

Remove the value text

I can't seem to hide the value text that follows when switching value.
How can i hide it?

Thumb is not synced with slider

Hello when i use custom renderThumb, thumb will get an offset from the slider as the image below shows when progress is 100 thumb should be exactly on the last step but it has a margin!
Screenshot 2024-04-30 at 11 57 57β€―PM

this my render thumb :

         renderThumb={() => (
                <View
                  style={{
                    height: 10,
                    width: 10,
                    backgroundColor: theme.background,
                    borderWidth: 1,
                    borderColor:
                      orderSide === 'buy' ? theme.marketGreen : theme.marketRed,
                    transform: [{rotate: '45deg'}],
                  }}
                />
              )}

adding testID for props

Hello thank you for your awesome slider.

I was wonder if it is possible to add testID on the slider component?

React not defined

When using web React is not defined in lib/module/ballon.js.
You use the function React but its not imported at line 1.

Its:
import { forwardRef, useImperativeHandle, useRef, memo } from 'react';

But should be:
import React, { forwardRef, useImperativeHandle, useRef, memo } from 'react';

Slider is not interactive, can't move it

I followed the simple instructions on this page: https://www.npmjs.com/package/react-native-awesome-slider

I'm using expo sdk v50, I've installed everything according to instructions on above page, to quickly get going I've just added this in my app:

import { useSharedValue } from 'react-native-reanimated';
import { Slider } from 'react-native-awesome-slider';

export const Example = () => {
  const progress = useSharedValue(30);
  const min = useSharedValue(0);
  const max = useSharedValue(100);
  return (
    <Slider
      style={styles.container}
      progress={progress}
      minimumValue={min}
      maximumValue={max}
    />
  );
};

and also wrapped at relevant place with GestureHandlerRootView.

Finally the Slider would render on my device (Pixel 7 Pro) without errors, but now while it looks "awesome", it's dead, nothing happens when I try to pull it. And I have no clue if its a bug or something else.

app

Add step prop

Thanks for your great package!

As many other sliders it would be nice to have a step prop (for a discrete slider)

The value of the progress bar is not refreshing.

When setting the value of progress for the first time, it takes effect. However, when I retrieve data from the server and want to display it on the progress bar, the progress does not refresh. How can I solve this issue?

snapToStep doesn't work, the progress bar stays at the same place but the value changes

Hello, I'm having a weird issue, when setting steps to the slider, it keeps staying to the place it's been at originally.

After a few investigations into the library code, I found that the issue is due to this part of the code

Screenshot 2023-09-01 at 16 45 37

For a reason I did not crack yet if you comment the whole "if snappingEnabled" statment, it works as intended.
I logged currentWidth, and especially currentIndex but they are always the same, that's where there is the actual problem.

Do you have any idea ?

Here is my code for you to reproduce something similar

  const [value, setValue] = useState(2500);


  return (
    <View style={{flex: 1, padding: 50}}>
      <Text>{value}</Text>
      <SliderInput
        maxValue={3000}
        minValue={0}
        initialValue={value}
        setValue={setValue}
      />
    </View>
  );

SliderInput:

 <Slider
      style={styles.container}
      containerStyle={{borderRadius: 8}}
      progress={progress}
      minimumValue={min}
      maximumValue={max}
      renderBubble={() => null}
      onValueChange={value => {
        setValue(Math.round(value));
      }}
      sliderHeight={12}
      renderThumb={() => <View style={styles.knob} />}
      theme={{
        maximumTrackTintColor: mainTheme.colors.lightGrey,
        minimumTrackTintColor: mainTheme.colors.primary,
      }}
      step={6}
      snapToStep={true}
    />

Here is the behavior observed

Screen.Recording.2023-09-01.at.16.51.10.mov

Keyboard getting opened On slider touch

import { StyleSheet, Text, View, TextInput } from 'react-native'
import React from 'react'
import { useSharedValue } from 'react-native-reanimated';
import { Slider } from 'react-native-awesome-slider';

const AnimatedSlider = () => {
  const progress = useSharedValue(30);
  const min = useSharedValue(0);
  const max = useSharedValue(100);
  return (
    <>
      <Slider
        style={styles.container}
        progress={progress}
        minimumValue={min}
        maximumValue={max}
        cache={true}
        heartbeat={true}
        hapticMode='step'
      />
    </>
  );
}

export default AnimatedSlider

const styles = StyleSheet.create({
  container: {
    flex: 1,
    minWidth: 300,
    backgroundColor: "skyblue"
  }
})

On opening the screen containing this slider, it automatically opens a keyboard, there is not text inputs present in entire screen.
I tried to look into the package code I guess TextInput from packages's BubbleComponent is getting focused.
Here's a code from your module.

<Animated.View
          style={{
            ...BUBBLE_STYLE,
            backgroundColor: color,
            maxWidth: bubbleMaxWidth,
          }}
        >
          <TextInput
            ref={textRef}
            textAlign="center"
            style={[styles.textStyle, { color: textColor }, textStyle]}
            defaultValue=""
            caretHidden
          />
        </Animated.View>

[2.1.1] Thumb not moving after updating from 2.0.8 to 2.1.1

Hi @alantoa, after upgrading from 2.0.8 to 2.1.1 the thumb stopped moving while playing audio files, downgrading to 2.0.8 works as expected. I've seen you've made some perfomance improvements using refs, I believe the ref value isn't updating the UI (at least in my case where progress is an event and not static thumb movement).

This is how I use it:

import React, { useCallback, useMemo } from "react";
import isEqual from "react-fast-compare";
import { Platform, StyleSheet, View } from "react-native";
import { Slider } from "react-native-awesome-slider";
import Animated, { useDerivedValue, useSharedValue } from "react-native-reanimated";
import { breadcrumb } from "utils/utils";

import { msToHMS, pausePlayer, seekPlayer } from "../AudioManager";

interface IProps {
  disabled: boolean;
  duration: number | undefined;
  messagePosition: string;
  onSeekComplete: (position: number, duration: number) => Promise<void>;
  progress: Animated.SharedValue<number>;
  themeContext: any;
}

const TrackPlayerProgress = ({
  disabled,
  duration,
  messagePosition,
  progress,
  themeContext
}: IProps) => {
  const isScrubbing = useSharedValue(false);
  const minValue = useSharedValue(0);

  const maxValue = useDerivedValue(() => {
    return duration;
  }, [duration]);

  const pauseAudio = useCallback(async () => {
    breadcrumb("User Paused Audio Message");
    await pausePlayer();
  }, []);

  const onSlidingStart = useCallback(async () => {
    await pauseAudio();
  }, [pauseAudio]);

  const onSlidingComplete = (slideValue: number) => {
    seekPlayer(slideValue).then(() => {
      isScrubbing.value = false;
    });
  };

  const theme = useMemo(
    () => ({
      minimumTrackTintColor:
        messagePosition === "right"
          ? themeContext.chatScreen.audioPlayer.right.minimumTrack
          : themeContext.chatScreen.audioPlayer.left.minimumTrack,
      maximumTrackTintColor:
        messagePosition === "right"
          ? themeContext.chatScreen.audioPlayer.right.track
          : themeContext.chatScreen.audioPlayer.left.track,
      bubbleBackgroundColor: themeContext.chatScreen.audioPlayer.timeBubble,
      bubbleTextColor: themeContext.chatScreen.audioPlayer.timeBubbleText
    }),
    [messagePosition, themeContext]
  );

  return (
    <View style={styles.container}>
      <View style={styles.sliderInnerContainer}>
        <Slider
          bubble={msToHMS}
          disableTrackFollow
          bubbleTranslateY={Platform.OS === "ios" ? -20 : -30}
          containerStyle={styles.sliderContainer}
          disable={disabled}
          isScrubbing={isScrubbing}
          // @ts-ignore
          maximumValue={maxValue}
          minimumValue={minValue}
          onSlidingComplete={onSlidingComplete}
          onSlidingStart={onSlidingStart}
          progress={progress}
          sliderHeight={5}
          style={styles.slider}
          thumbWidth={20}
          theme={theme}
        />
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    alignItems: "center",
    marginLeft: 5,
    marginRight: 15
  },
  sliderInnerContainer: {
    maxWidth: 500,
    position: "relative",
    width: "100%"
  },
  sliderContainer: {
    borderRadius: 50
  },
  slider: {
    borderRadius: 12,
    marginBottom: 14,
    marginTop: 16
  }
});
export default React.memo(TrackPlayerProgress, isEqual);

progress prop is const progress = useSharedValue(0); and it's been incremented by the audio player progress event.

Let me know if you need any additional info, I will go through the changes you've introduced and let you know if I figure out what is going on.

isScrubbing is not set to false when tapping on the slider or thumb

The isScrubbing value is set to true and never false again when tapping on the slider. This makes styling the slider based on the current interaction state unreliable.

It appears that the issue is that the tap even gesture only sets the values to true and never false again. I would expect it to either set the value to false, or set the value to true, then to false again on a delay. (After some testing, I would recommend no delay)

The current workaround is to manually update the value yourself by passing an onSlidingComplete function but keeping that manually updated defeats the purpose of providing a value.

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.