Merge pull request #426 from ThibG/glitch-soc/features/display-focal-points
Honor focal points when displaying media
This commit is contained in:
		
						commit
						40006bcd03
					
				| @ -147,6 +147,11 @@ class Item extends React.PureComponent { | |||||||
|       const srcSet = hasSize ? `${originalUrl} ${originalWidth}w, ${previewUrl} ${previewWidth}w` : null; |       const srcSet = hasSize ? `${originalUrl} ${originalWidth}w, ${previewUrl} ${previewWidth}w` : null; | ||||||
|       const sizes  = hasSize ? `(min-width: 1025px) ${320 * (width / 100)}px, ${width}vw` : null; |       const sizes  = hasSize ? `(min-width: 1025px) ${320 * (width / 100)}px, ${width}vw` : null; | ||||||
| 
 | 
 | ||||||
|  |       const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0; | ||||||
|  |       const focusY = attachment.getIn(['meta', 'focus', 'y']) || 0; | ||||||
|  |       const x      = ((focusX /  2) + .5) * 100; | ||||||
|  |       const y      = ((focusY / -2) + .5) * 100; | ||||||
|  | 
 | ||||||
|       thumbnail = ( |       thumbnail = ( | ||||||
|         <a |         <a | ||||||
|           className='media-gallery__item-thumbnail' |           className='media-gallery__item-thumbnail' | ||||||
| @ -154,7 +159,14 @@ class Item extends React.PureComponent { | |||||||
|           onClick={this.handleClick} |           onClick={this.handleClick} | ||||||
|           target='_blank' |           target='_blank' | ||||||
|         > |         > | ||||||
|           <img className={letterbox ? 'letterbox' : null} src={previewUrl} srcSet={srcSet} sizes={sizes} alt={attachment.get('description')} title={attachment.get('description')} /> |           <img | ||||||
|  |             className={letterbox ? 'letterbox' : null} | ||||||
|  |             src={previewUrl} | ||||||
|  |             srcSet={srcSet} | ||||||
|  |             sizes={sizes} | ||||||
|  |             alt={attachment.get('description')} | ||||||
|  |             title={attachment.get('description')} | ||||||
|  |             style={{ objectPosition: `${x}% ${y}%` }} /> | ||||||
|         </a> |         </a> | ||||||
|       ); |       ); | ||||||
|     } else if (attachment.get('type') === 'gifv') { |     } else if (attachment.get('type') === 'gifv') { | ||||||
| @ -225,30 +237,59 @@ export default class MediaGallery extends React.PureComponent { | |||||||
|     this.props.onOpenMedia(this.props.media, index); |     this.props.onOpenMedia(this.props.media, index); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   handleRef = (node) => { | ||||||
|  |     if (node && this.isStandaloneEligible()) { | ||||||
|  |       // offsetWidth triggers a layout, so only calculate when we need to
 | ||||||
|  |       this.setState({ | ||||||
|  |         width: node.offsetWidth, | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   isStandaloneEligible() { | ||||||
|  |     const { media, standalone } = this.props; | ||||||
|  |     return standalone && media.size === 1 && media.getIn([0, 'meta', 'small', 'aspect']); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   render () { |   render () { | ||||||
|     const { |     const { media, intl, sensitive, letterbox, fullwidth } = this.props; | ||||||
|       handleClick, |     const { width, visible } = this.state; | ||||||
|       handleOpen, |  | ||||||
|     } = this; |  | ||||||
|     const { |  | ||||||
|       fullwidth, |  | ||||||
|       intl, |  | ||||||
|       letterbox, |  | ||||||
|       media, |  | ||||||
|       sensitive, |  | ||||||
|       standalone, |  | ||||||
|     } = this.props; |  | ||||||
|     const { visible } = this.state; |  | ||||||
|     const size = media.take(4).size; |     const size = media.take(4).size; | ||||||
|  | 
 | ||||||
|  |     let children; | ||||||
|  | 
 | ||||||
|  |     const style = {}; | ||||||
|  | 
 | ||||||
|  |     if (this.isStandaloneEligible() && width) { | ||||||
|  |       style.height = width / this.props.media.getIn([0, 'meta', 'small', 'aspect']); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!visible) { | ||||||
|  |       let warning = <FormattedMessage {...(sensitive ? messages.warning : messages.hidden)} />; | ||||||
|  | 
 | ||||||
|  |       children = ( | ||||||
|  |         <button className='media-spoiler' type='button' onClick={this.handleOpen}> | ||||||
|  |           <span className='media-spoiler__warning'>{warning}</span> | ||||||
|  |           <span className='media-spoiler__trigger'><FormattedMessage {...messages.toggle} /></span> | ||||||
|  |         </button> | ||||||
|  |       ); | ||||||
|  |     } else { | ||||||
|  |       if (this.isStandaloneEligible()) { | ||||||
|  |         children = <Item standalone attachment={media.get(0)} onClick={this.handleClick} />; | ||||||
|  |       } else { | ||||||
|  |         children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} index={i} size={size} letterbox={letterbox} />); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     const computedClass = classNames('media-gallery', `size-${size}`, { 'full-width': fullwidth }); |     const computedClass = classNames('media-gallery', `size-${size}`, { 'full-width': fullwidth }); | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <div className={computedClass}> |       <div className={computedClass} style={style} ref={this.handleRef}> | ||||||
|         {visible ? ( |         {visible ? ( | ||||||
|           <div className='sensitive-info'> |           <div className='sensitive-info'> | ||||||
|             <IconButton |             <IconButton | ||||||
|               icon='eye' |               icon='eye' | ||||||
|               onClick={handleOpen} |               onClick={this.handleOpen} | ||||||
|               overlay |               overlay | ||||||
|               title={intl.formatMessage(messages.toggle_visible)} |               title={intl.formatMessage(messages.toggle_visible)} | ||||||
|             /> |             /> | ||||||
| @ -259,46 +300,8 @@ export default class MediaGallery extends React.PureComponent { | |||||||
|             ) : null} |             ) : null} | ||||||
|           </div> |           </div> | ||||||
|         ) : null} |         ) : null} | ||||||
|         {function () { | 
 | ||||||
|           switch (true) { |         {children} | ||||||
|           case !visible: |  | ||||||
|             return ( |  | ||||||
|               <button |  | ||||||
|                 className='media-spoiler' |  | ||||||
|                 type='button' |  | ||||||
|                 onClick={handleOpen} |  | ||||||
|               > |  | ||||||
|                 <span className='media-spoiler__warning'> |  | ||||||
|                   <FormattedMessage {...(sensitive ? messages.warning : messages.hidden)} /> |  | ||||||
|                 </span> |  | ||||||
|                 <span className='media-spoiler__trigger'> |  | ||||||
|                   <FormattedMessage {...messages.toggle} /> |  | ||||||
|                 </span> |  | ||||||
|               </button> |  | ||||||
|             ); |  | ||||||
|           case standalone && media.size === 1 && !!media.getIn([0, 'meta', 'small', 'aspect']): |  | ||||||
|             return ( |  | ||||||
|               <Item |  | ||||||
|                 attachment={media.get(0)} |  | ||||||
|                 onClick={handleClick} |  | ||||||
|                 standalone |  | ||||||
|               /> |  | ||||||
|             ); |  | ||||||
|           default: |  | ||||||
|             return media.take(4).map( |  | ||||||
|               (attachment, i) => ( |  | ||||||
|                 <Item |  | ||||||
|                   attachment={attachment} |  | ||||||
|                   index={i} |  | ||||||
|                   key={attachment.get('id')} |  | ||||||
|                   letterbox={letterbox} |  | ||||||
|                   onClick={handleClick} |  | ||||||
|                   size={size} |  | ||||||
|                 /> |  | ||||||
|               ) |  | ||||||
|             ); |  | ||||||
|           } |  | ||||||
|         }()} |  | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | |||||||
| import Avatar from 'flavours/glitch/components/avatar'; | import Avatar from 'flavours/glitch/components/avatar'; | ||||||
| import DisplayName from 'flavours/glitch/components/display_name'; | import DisplayName from 'flavours/glitch/components/display_name'; | ||||||
| import StatusContent from 'flavours/glitch/components/status_content'; | import StatusContent from 'flavours/glitch/components/status_content'; | ||||||
| import StatusGallery 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, FormattedNumber } from 'react-intl'; | ||||||
| @ -69,7 +69,8 @@ export default class DetailedStatus extends ImmutablePureComponent { | |||||||
|         mediaIcon = 'video-camera'; |         mediaIcon = 'video-camera'; | ||||||
|       } else { |       } else { | ||||||
|         media = ( |         media = ( | ||||||
|           <StatusGallery |           <MediaGallery | ||||||
|  |             standalone | ||||||
|             sensitive={status.get('sensitive')} |             sensitive={status.get('sensitive')} | ||||||
|             media={status.get('media_attachments')} |             media={status.get('media_attachments')} | ||||||
|             letterbox={settings.getIn(['media', 'letterbox'])} |             letterbox={settings.getIn(['media', 'letterbox'])} | ||||||
|  | |||||||
| @ -29,7 +29,7 @@ const formatTime = secondsNum => { | |||||||
|   return (hours === '00' ? '' : `${hours}:`) + `${minutes}:${seconds}`; |   return (hours === '00' ? '' : `${hours}:`) + `${minutes}:${seconds}`; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const findElementPosition = el => { | export const findElementPosition = el => { | ||||||
|   let box; |   let box; | ||||||
| 
 | 
 | ||||||
|   if (el.getBoundingClientRect && el.parentNode) { |   if (el.getBoundingClientRect && el.parentNode) { | ||||||
| @ -60,7 +60,7 @@ const findElementPosition = el => { | |||||||
|   }; |   }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const getPointerPosition = (el, event) => { | export const getPointerPosition = (el, event) => { | ||||||
|   const position = {}; |   const position = {}; | ||||||
|   const box = findElementPosition(el); |   const box = findElementPosition(el); | ||||||
|   const boxW = el.offsetWidth; |   const boxW = el.offsetWidth; | ||||||
| @ -76,7 +76,7 @@ const getPointerPosition = (el, event) => { | |||||||
|     pageY = event.changedTouches[0].pageY; |     pageY = event.changedTouches[0].pageY; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   position.y = Math.max(0, Math.min(1, ((boxY - pageY) + boxH) / boxH)); |   position.y = Math.max(0, Math.min(1, (pageY - boxY) / boxH)); | ||||||
|   position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW)); |   position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW)); | ||||||
| 
 | 
 | ||||||
|   return position; |   return position; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user