vydimitrov / react-countdown-circle-timer

Lightweight React/React Native countdown timer component with color and progress animation based on SVG

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to use gradient colors in RN projects

1280103995 opened this issue · comments

commented

I looked at the examples for the web version, but it didn't solve my problem.

I run react-native init myapp to create a RN project, and run yarn add react-native-svg react-native-countdown-circle-timer@3.0.8 to add dependencies.

This is my App.js

import React from 'react';
import {Text, View} from 'react-native';
import Svg, {Defs, LinearGradient, Stop} from "react-native-svg";
import { CountdownCircleTimer } from 'react-native-countdown-circle-timer'

export default function App() {
  return (
    <View>
      <Svg width="0" height="0" >
        <Defs>
          <LinearGradient id="your-unique-id" x1="1" y1="0" x2="0" y2="0">
            <Stop offset="5%" stopColor="gold"/>
            <Stop offset="95%" stopColor="red"/>
          </LinearGradient>
        </Defs>
      </Svg>
      <CountdownCircleTimer
        colors="url(#your-unique-id)"
        size={200}
        strokeWidth={20}
        duration={60}
        initialRemainingTime={25}>
        {({remainingTime}) => <Text>{remainingTime}</Text>}
      </CountdownCircleTimer>
    </View>
  );
}

Hey @1280103995, it seems your right. I open an issue with react-native-svg to clarify that.

Meanwhile I recommend using the hook and build your own component and gradient. It should look like something like this:

import { useCountdown } from 'react-native-countdown-circle-timer'

const Component = () => {
const {
  path,
  pathLength,
  stroke,
  strokeDashoffset,
  remainingTime,
  elapsedTime,
  size,
  strokeWidth,
} = useCountdown({ isPlaying: true, duration: 7, colors: '#abc' })

return (
<View style={getWrapperStyle(size) as StyleProp<ViewStyle>}>
    <Svg width={size} height={size}>
       <Defs>
        <LinearGradient id="your-unique-id" x1="1" y1="0" x2="0" y2="0">
          <Stop offset="5%" stopColor="gold"/>
          <Stop offset="95%" stopColor="red"/>
        </LinearGradient>
      </Defs>
      <Path
        d={path}
        fill="none"
        stroke={trailColor ?? '#d9d9d9'}
        strokeWidth={trailStrokeWidth ?? strokeWidth}
      />
      {elapsedTime !== duration && (
        <Path
          d={path}
          fill="none"
          sroke="url(#your-unique-id)"
          strokeLinecap={strokeLinecap ?? 'round'}
          strokeWidth={strokeWidth}
          strokeDasharray={pathLength}
          strokeDashoffset={strokeDashoffset}
        />
      )}
    </Svg>
    {typeof children === 'function' && (
      <View style={timeStyle as StyleProp<ViewStyle>}>
        {children({ remainingTime, elapsedTime, color: stroke })}
       </View>
    )}
</View>
)
}

This one is going to work since the gradient and the consumer are under the same SVG element.

commented

I tried using your code, but the gradient still doesn't show.

import React from 'react';
import {Text, View} from 'react-native';
import Svg, {Defs, LinearGradient, Path, Stop} from 'react-native-svg';
import { useCountdown } from 'react-native-countdown-circle-timer'

export default function App() {
  const duration = 60;
  const {
    path,
    pathLength,
    stroke,
    strokeDashoffset,
    remainingTime,
    elapsedTime,
    size,
    strokeWidth,
  } = useCountdown({ isPlaying: true, duration, colors: '#ffabef', initialRemainingTime: 25 })
  return (
    <View style={{width: size, height: size}}>
      <Svg width={size} height={size}>
        <Defs>
          <LinearGradient id="your-unique-id" x1="1" y1="0" x2="0" y2="0">
            <Stop offset="5%" stopColor="gold"/>
            <Stop offset="95%" stopColor="red"/>
          </LinearGradient>
        </Defs>
        <Path
          d={path}
          fill="none"
          stroke={'#d9d9d9'}
          strokeWidth={strokeWidth}
        />
        {elapsedTime !== duration && (
          <Path
            d={path}
            fill="none"
            sroke="url(#your-unique-id)"
            strokeLinecap={'butt'}
            strokeWidth={strokeWidth}
            strokeDasharray={pathLength}
            strokeDashoffset={strokeDashoffset}
          />
        )}
      </Svg>
    </View>
  );
}

By the way, my network is poor and I often can't open the Github website, so sometimes the reply will be slow.

commented

Shockingly, I copied two files from v2: DefsLinearGradient and colorsValidator for the gradient component and it worked just fine.

App.js

import React from 'react';
import {View} from 'react-native';
import Svg, {Path} from 'react-native-svg';
import { useCountdown } from 'react-native-countdown-circle-timer'
import {DefsLinearGradient} from './src/DefsLinearGradient';

export default function App() {
  const duration = 60;
  const colors = [
      ['#6E40AA', 0.33],
      ['#DF40A1', 0.33],
      ['#FF704E', 0.34],
  ]

  const {
    path,
    pathLength,
    stroke,
    strokeDashoffset,
    remainingTime,
    elapsedTime,
    size,
    strokeWidth,
  } = useCountdown({ isPlaying: true, duration, colors, initialRemainingTime: 59 })

  return (
    <View style={{width: size, height: size}}>
      <Svg width={size} height={size}>
        <DefsLinearGradient
          colors={colors}
          gradientId={'your-unique-id'}
        />
        <Path
          d={path}
          fill="none"
          stroke={'#d9d9d9'}
          strokeWidth={strokeWidth}
        />
        {elapsedTime !== duration && (
          <Path
            d={path}
            fill="none"
            stroke="url(#your-unique-id)"
            strokeLinecap={'butt'}
            strokeWidth={strokeWidth}
            strokeDasharray={pathLength}
            strokeDashoffset={strokeDashoffset}
          />
        )}
      </Svg>
    </View>
  );
}

Hmm, that is very strange. The only difference I see is that the offset is define in numbers from 0 to 1 and colors are in HEX format. Otherwise it seems the same.

I updated the docs to reflect the new way to build the gradient. I do not think that we gonna have an answer on the issue I opened with react-native-svg and as we can see it does not work if the gradient detentions is not in the same SVG as the usage of it.

Here is link to the demo on the recommended way to do t he gradient https://snack.expo.dev/@dimitrov/react-native-countdown-circle-timer-gradient?platform=ios

commented

Why we can't use gradient without using hooks?

Also how to restart timer which is based on useCountDown hook

Why we can't use gradient without using hooks?

Also how to restart timer which is based on useCountDown hook

+1
I'd also like to use a gradient and at the same time be able to restart the timer

Hey you can restart the timer the same way you do it with the component, by passing a key. So below is your CountdownCircleTimer:

// src/components/timer - this is your folder for shared components
export const CountdownCircleTimer = ({ duration, isPlaying, .... }) => {
  const {
    path,
    pathLength,
    stroke,
    strokeDashoffset,
    remainingTime,
    elapsedTime,
    size,
    strokeWidth,
  } = useCountdown({ isPlaying: true, duration, colors: 'url(#your-unique-id)' })

  return (
    <View style={styles.container}>
      <View style={{ width: size, height: size, position: 'relative' }}>
        <Svg width={size} height={size}>
          <Defs>
            <LinearGradient id="your-unique-id" x1="1" y1="0" x2="0" y2="0">
              <Stop offset="5%" stopColor="gold"/>
              <Stop offset="95%" stopColor="red"/>
            </LinearGradient>
          </Defs>
          <Path
            d={path}
            fill="none"
            stroke="#d9d9d9"
            strokeWidth={strokeWidth}
          />
          {elapsedTime !== duration && (
            <Path
              d={path}
              fill="none"
              stroke={stroke}
              strokeLinecap="butt"
              strokeWidth={strokeWidth}
              strokeDasharray={pathLength}
              strokeDashoffset={strokeDashoffset}
            />
          )}
        </Svg>
        <View style={styles.time}>
          <Text style={{ fontSize: 36 }}>{remainingTime}</Text>
        </View>
      </View>
    </View>
  );
}

Then in your App you just use it as it was before

import { CountdownCircleTimer } from 'src/components/timer '

<CountdownCircleTimer key="some-new-key-to-restart" duration={10} isPlaying />

Does it help?

commented

Yeap, it helps. I already figured an answer by myself, but thanks 🙏

Worked for me as well. Thanks!