[Glitch] Add number animations
Port 76f1ed834efd3b58b6ebc8e951b661bbc1b7bf9b to glitch-soc Signed-off-by: Thibaut Girka <thib@sitedethib.com>
This commit is contained in:
		
							parent
							
								
									3c4bd03949
								
							
						
					
					
						commit
						f1e4738f81
					
				
							
								
								
									
										47
									
								
								app/javascript/flavours/glitch/components/animated_number.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								app/javascript/flavours/glitch/components/animated_number.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | |||||||
|  | import React from 'react'; | ||||||
|  | import PropTypes from 'prop-types'; | ||||||
|  | import { FormattedNumber } from 'react-intl'; | ||||||
|  | import TransitionMotion from 'react-motion/lib/TransitionMotion'; | ||||||
|  | import spring from 'react-motion/lib/spring'; | ||||||
|  | import { reduceMotion } from 'flavours/glitch/util/initial_state'; | ||||||
|  | 
 | ||||||
|  | export default class AnimatedNumber extends React.PureComponent { | ||||||
|  | 
 | ||||||
|  |   static propTypes = { | ||||||
|  |     value: PropTypes.number.isRequired, | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   willEnter () { | ||||||
|  |     return { y: -1 }; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   willLeave () { | ||||||
|  |     return { y: spring(1, { damping: 35, stiffness: 400 }) }; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   render () { | ||||||
|  |     const { value } = this.props; | ||||||
|  | 
 | ||||||
|  |     if (reduceMotion) { | ||||||
|  |       return <FormattedNumber value={value} />; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const styles = [{ | ||||||
|  |       key: value, | ||||||
|  |       style: { y: spring(0, { damping: 35, stiffness: 400 }) }, | ||||||
|  |     }]; | ||||||
|  | 
 | ||||||
|  |     return ( | ||||||
|  |       <TransitionMotion styles={styles} willEnter={this.willEnter} willLeave={this.willLeave}> | ||||||
|  |         {items => ( | ||||||
|  |           <span className='animated-number'> | ||||||
|  |             {items.map(({ key, style }) => ( | ||||||
|  |               <span key={key} style={{ position: style.y > 0 ? 'absolute' : 'static', transform: `translateY(${style.y * 100}%)` }}><FormattedNumber value={key} /></span> | ||||||
|  |             ))} | ||||||
|  |           </span> | ||||||
|  |         )} | ||||||
|  |       </TransitionMotion> | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -5,13 +5,14 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | |||||||
| import PropTypes from 'prop-types'; | import PropTypes from 'prop-types'; | ||||||
| import IconButton from 'flavours/glitch/components/icon_button'; | import IconButton from 'flavours/glitch/components/icon_button'; | ||||||
| import Icon from 'flavours/glitch/components/icon'; | import Icon from 'flavours/glitch/components/icon'; | ||||||
| import { defineMessages, injectIntl, FormattedMessage, FormattedDate, FormattedNumber } from 'react-intl'; | import { defineMessages, injectIntl, FormattedMessage, FormattedDate } from 'react-intl'; | ||||||
| import { autoPlayGif } from 'flavours/glitch/util/initial_state'; | import { autoPlayGif } from 'flavours/glitch/util/initial_state'; | ||||||
| import elephantUIPlane from 'mastodon/../images/elephant_ui_plane.svg'; | import elephantUIPlane from 'mastodon/../images/elephant_ui_plane.svg'; | ||||||
| import { mascot } from 'flavours/glitch/util/initial_state'; | import { mascot } from 'flavours/glitch/util/initial_state'; | ||||||
| import unicodeMapping from 'flavours/glitch/util/emoji/emoji_unicode_mapping_light'; | import unicodeMapping from 'flavours/glitch/util/emoji/emoji_unicode_mapping_light'; | ||||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||||
| import EmojiPickerDropdown from 'flavours/glitch/features/emoji_picker'; | import EmojiPickerDropdown from 'flavours/glitch/features/emoji_picker'; | ||||||
|  | import AnimatedNumber from 'flavours/glitch/components/animated_number'; | ||||||
| 
 | 
 | ||||||
| const messages = defineMessages({ | const messages = defineMessages({ | ||||||
|   close: { id: 'lightbox.close', defaultMessage: 'Close' }, |   close: { id: 'lightbox.close', defaultMessage: 'Close' }, | ||||||
| @ -225,7 +226,7 @@ class Reaction extends ImmutablePureComponent { | |||||||
|     return ( |     return ( | ||||||
|       <button className={classNames('reactions-bar__item', { active: reaction.get('me') })} onClick={this.handleClick} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} title={`:${shortCode}:`}> |       <button className={classNames('reactions-bar__item', { active: reaction.get('me') })} onClick={this.handleClick} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} title={`:${shortCode}:`}> | ||||||
|         <span className='reactions-bar__item__emoji'><Emoji hovered={this.state.hovered} emoji={reaction.get('name')} emojiMap={this.props.emojiMap} /></span> |         <span className='reactions-bar__item__emoji'><Emoji hovered={this.state.hovered} emoji={reaction.get('name')} emojiMap={this.props.emojiMap} /></span> | ||||||
|         <span className='reactions-bar__item__count'><FormattedNumber value={reaction.get('count')} /></span> |         <span className='reactions-bar__item__count'><AnimatedNumber value={reaction.get('count')} /></span> | ||||||
|       </button> |       </button> | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ import StatusContent from 'flavours/glitch/components/status_content'; | |||||||
| import MediaGallery from 'flavours/glitch/components/media_gallery'; | import MediaGallery from 'flavours/glitch/components/media_gallery'; | ||||||
| import AttachmentList from 'flavours/glitch/components/attachment_list'; | import AttachmentList from 'flavours/glitch/components/attachment_list'; | ||||||
| import { Link } from 'react-router-dom'; | import { Link } from 'react-router-dom'; | ||||||
| import { FormattedDate, FormattedNumber } from 'react-intl'; | import { FormattedDate } from 'react-intl'; | ||||||
| import Card from './card'; | import Card from './card'; | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| import Video from 'flavours/glitch/features/video'; | import Video from 'flavours/glitch/features/video'; | ||||||
| @ -17,6 +17,7 @@ import scheduleIdleTask from 'flavours/glitch/util/schedule_idle_task'; | |||||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||||
| import PollContainer from 'flavours/glitch/containers/poll_container'; | import PollContainer from 'flavours/glitch/containers/poll_container'; | ||||||
| import Icon from 'flavours/glitch/components/icon'; | import Icon from 'flavours/glitch/components/icon'; | ||||||
|  | import AnimatedNumber from 'flavours/glitch/components/animated_number'; | ||||||
| 
 | 
 | ||||||
| export default class DetailedStatus extends ImmutablePureComponent { | export default class DetailedStatus extends ImmutablePureComponent { | ||||||
| 
 | 
 | ||||||
| @ -204,7 +205,7 @@ export default class DetailedStatus extends ImmutablePureComponent { | |||||||
|         <Link to={`/statuses/${status.get('id')}/reblogs`} className='detailed-status__link'> |         <Link to={`/statuses/${status.get('id')}/reblogs`} className='detailed-status__link'> | ||||||
|           <Icon id={reblogIcon} /> |           <Icon id={reblogIcon} /> | ||||||
|           <span className='detailed-status__reblogs'> |           <span className='detailed-status__reblogs'> | ||||||
|             <FormattedNumber value={status.get('reblogs_count')} /> |             <AnimatedNumber value={status.get('reblogs_count')} /> | ||||||
|           </span> |           </span> | ||||||
|         </Link> |         </Link> | ||||||
|       ); |       ); | ||||||
| @ -213,7 +214,7 @@ export default class DetailedStatus extends ImmutablePureComponent { | |||||||
|         <a href={`/interact/${status.get('id')}?type=reblog`} className='detailed-status__link' onClick={this.handleModalLink}> |         <a href={`/interact/${status.get('id')}?type=reblog`} className='detailed-status__link' onClick={this.handleModalLink}> | ||||||
|           <Icon id={reblogIcon} /> |           <Icon id={reblogIcon} /> | ||||||
|           <span className='detailed-status__reblogs'> |           <span className='detailed-status__reblogs'> | ||||||
|             <FormattedNumber value={status.get('reblogs_count')} /> |             <AnimatedNumber value={status.get('reblogs_count')} /> | ||||||
|           </span> |           </span> | ||||||
|         </a> |         </a> | ||||||
|       ); |       ); | ||||||
| @ -224,7 +225,7 @@ export default class DetailedStatus extends ImmutablePureComponent { | |||||||
|         <Link to={`/statuses/${status.get('id')}/favourites`} className='detailed-status__link'> |         <Link to={`/statuses/${status.get('id')}/favourites`} className='detailed-status__link'> | ||||||
|           <Icon id='star' /> |           <Icon id='star' /> | ||||||
|           <span className='detailed-status__favorites'> |           <span className='detailed-status__favorites'> | ||||||
|             <FormattedNumber value={status.get('favourites_count')} /> |             <AnimatedNumber value={status.get('favourites_count')} /> | ||||||
|           </span> |           </span> | ||||||
|         </Link> |         </Link> | ||||||
|       ); |       ); | ||||||
| @ -233,7 +234,7 @@ export default class DetailedStatus extends ImmutablePureComponent { | |||||||
|         <a href={`/interact/${status.get('id')}?type=favourite`} className='detailed-status__link' onClick={this.handleModalLink}> |         <a href={`/interact/${status.get('id')}?type=favourite`} className='detailed-status__link' onClick={this.handleModalLink}> | ||||||
|           <Icon id='star' /> |           <Icon id='star' /> | ||||||
|           <span className='detailed-status__favorites'> |           <span className='detailed-status__favorites'> | ||||||
|             <FormattedNumber value={status.get('favourites_count')} /> |             <AnimatedNumber value={status.get('favourites_count')} /> | ||||||
|           </span> |           </span> | ||||||
|         </a> |         </a> | ||||||
|       ); |       ); | ||||||
|  | |||||||
| @ -168,10 +168,10 @@ | |||||||
|     &.active { |     &.active { | ||||||
|       transition: all 100ms ease-in; |       transition: all 100ms ease-in; | ||||||
|       transition-property: background-color, color; |       transition-property: background-color, color; | ||||||
|       background-color: mix(lighten($ui-base-color, 12%), $ui-highlight-color, 90%); |       background-color: mix(lighten($ui-base-color, 12%), $ui-highlight-color, 80%); | ||||||
| 
 | 
 | ||||||
|       .reactions-bar__item__count { |       .reactions-bar__item__count { | ||||||
|         color: $highlight-text-color; |         color: lighten($highlight-text-color, 8%); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -3,6 +3,14 @@ | |||||||
|   -ms-overflow-style: -ms-autohiding-scrollbar; |   -ms-overflow-style: -ms-autohiding-scrollbar; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .animated-number { | ||||||
|  |   display: inline-flex; | ||||||
|  |   flex-direction: column; | ||||||
|  |   align-items: stretch; | ||||||
|  |   overflow: hidden; | ||||||
|  |   position: relative; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| .link-button { | .link-button { | ||||||
|   display: block; |   display: block; | ||||||
|   font-size: 15px; |   font-size: 15px; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user