Parallax setup

This commit is contained in:
James Barnsley
2016-10-25 17:01:37 +13:00
parent 5372cd0334
commit ab9f317aed
15 changed files with 193 additions and 20 deletions

View File

Before

Width:  |  Height:  |  Size: 735 B

After

Width:  |  Height:  |  Size: 735 B

View File

@ -10,7 +10,9 @@ export default class ListItem extends React.Component{
}
handleClick(e){
browserHistory.push( this.props.link );
if( e.target.tagName.toLowerCase() !== 'a' ){
browserHistory.push( this.props.link );
}
}
render(){

33
src/js/components/Parallax.js Executable file
View File

@ -0,0 +1,33 @@
import React, { PropTypes } from 'react'
import * as helpers from '../helpers'
export default class Parallax extends React.Component{
constructor(props) {
super(props);
this.state = {
image: false
}
}
componentDidMount(){
this.setState({
image: helpers.SizedImages( this.props.images )
})
this.update();
}
update(){
// TODO: copy canvas rules in here from Spotmop
}
render(){
return (
<div className="parallax">
{ this.state.image ? <img src={this.state.image.huge} /> : null }
</div>
);
}
}

View File

@ -0,0 +1,29 @@
import React, { PropTypes } from 'react'
import { Link } from 'react-router'
import Thumbnail from './Thumbnail'
export default class RelatedArtistList extends React.Component{
constructor(props) {
super(props);
}
render(){
return (
<span className="related-artist-list">
{
this.props.artists.map( (artist, index) => {
var link = '/artist/' + artist.uri;
return (
<Link to={ link } key={artist.uri} className="artist">
<Thumbnail circle={true} size="small" images={artist.images} />
{ artist.name }
</Link>
);
})
}
</span>
);
}
}

View File

@ -1,5 +1,6 @@
import React, { PropTypes } from 'react'
import * as helpers from '../helpers'
export default class Thumbnail extends React.Component{
@ -24,22 +25,33 @@ export default class Thumbnail extends React.Component{
mapImageSizes(){
var images = this.props.images;
for( var i = 0; i < images.length; i++ ){
if( images[i].height > 800 ){
this.setState({ huge: images[i].url });
}else if( images[i].height > 600 ){
this.setState({ large: images[i].url });
}else if( images[i].height > 280 ){
this.setState({ medium: images[i].url });
}else{
this.setState({ small: images[i].url });
var state = this.state;
if( images.length <= 0 ){
state = {
small: require('../../images/no-image.svg'),
medium: require('../../images/no-image.svg'),
large: require('../../images/no-image.svg'),
huge: require('../../images/no-image.svg')
}
}else{
state = helpers.SizedImages( images );
}
this.setState( state );
}
render(){
var style = {
backgroundImage: 'url("'+this.state[this.props.size]+'")'
}
var className = 'thumbnail '+this.props.size;
if( this.props.circle ) className += ' circle';
return (
<img className={ 'thumbnail '+ this.props.size } src={ this.state[this.props.size] } />
<div className={className}>
<div className="image" style={style}></div>
</div>
);
}
}

32
src/js/helpers.js Executable file
View File

@ -0,0 +1,32 @@
export let SizedImages = function( images ){
var sizes = {
small: false,
medium: false,
large: false,
huge: false
}
if( images.length > 0 ){
for( var i = 0; i < images.length; i++ ){
if( images[i].height > 800 ){
sizes.huge = images[i].url;
}else if( images[i].height > 600 ){
sizes.large = images[i].url;
}else if( images[i].height > 280 ){
sizes.medium = images[i].url;
}else{
sizes.small = images[i].url;
}
}
if( !sizes.medium ) sizes.medium = sizes.small;
if( !sizes.large ) sizes.large = sizes.medium;
if( !sizes.huge ) sizes.huge = sizes.large;
}
return sizes;
}

View File

@ -199,9 +199,14 @@ export function getArtist( uri ){
Object.assign(artist, response);
}),
sendRequest( dispatch, getState, 'artists/'+ getFromUri('artistid', uri) +'/top-tracks?country='+getState().spotify.country )
sendRequest( dispatch, getState, 'artists/'+ getFromUri('artistid', uri) +'/top-tracks?country='+getState().spotify.country )
.then( response => {
Object.assign(artist, response);
}),
sendRequest( dispatch, getState, 'artists/'+ getFromUri('artistid', uri) +'/related-artists' )
.then( response => {
Object.assign(artist, response);
Object.assign(artist, { related_artists: response.artists });
})
).then( () => {

View File

@ -6,6 +6,9 @@ import { bindActionCreators } from 'redux'
import Header from '../components/Header'
import TrackList from '../components/TrackList'
import AlbumGrid from '../components/AlbumGrid'
import Thumbnail from '../components/Thumbnail'
import Parallax from '../components/Parallax'
import RelatedArtistList from '../components/RelatedArtistList'
import * as spotifyActions from '../services/spotify/actions'
@ -32,7 +35,10 @@ class Artist extends React.Component{
return (
<div>
<Header icon="mic" title={ this.props.spotify.artist.name } />
{ this.props.spotify.artist.images ? <Parallax images={ this.props.spotify.artist.images } /> : null }
{ this.props.spotify.artist.images ? <Thumbnail size="huge" images={ this.props.spotify.artist.images } /> : null }
<p>{ this.props.spotify.artist.followers.total.toLocaleString() } followers</p>
{ this.props.spotify.artist.related_artists ? <RelatedArtistList artists={ this.props.spotify.artist.related_artists } /> : null }
{ this.props.spotify.artist.tracks ? <TrackList tracks={ this.props.spotify.artist.tracks } /> : null }
{ this.props.spotify.artist_albums ? <AlbumGrid items={ this.props.spotify.artist_albums } /> : null }
</div>

View File

@ -67,7 +67,6 @@ class Queue extends React.Component{
title="Now playing"
/>
{ this.renderTrackInFocus() }
<h4>Other tracks</h4>
{ this.renderTrackList() }
</div>
);

View File

@ -48,7 +48,7 @@ class Settings extends React.Component{
<section>
<h3>Mopidy</h3>
<h3 className="underline">Mopidy</h3>
<form onSubmit={() => this.setMopidyConfig()}>
<label>
<span className="label">Host</span>
@ -61,7 +61,7 @@ class Settings extends React.Component{
<button type="submit">Apply</button>
</form>
<h3>Spotify</h3>
<h3 className="underline">Spotify</h3>
<form onSubmit={() => this.setSpotifyConfig()}>
<label>
<span className="label">Country</span>

View File

@ -7,9 +7,12 @@
@import 'global/reset';
@import 'global/forms';
@import 'components/images';
@import 'components/icon';
@import 'components/sidebar';
@import 'components/lists';
@import 'components/grid';
@import 'components/header';
@import 'views/artist';

View File

@ -0,0 +1,29 @@
.thumbnail {
width: 100%;
max-width: 300px;
overflow: hidden;
.image {
width: 100%;
padding-bottom: 100%;
position: relative;
background-repeat: no-repeat;
background-color: #DDDDDD;
background-position: 50% 50%;
background-size: cover;
}
&.circle {
border-radius: 50%;
}
}
.parallax {
height: 300px;
overflow: hidden;
.thumbnail {
max-width: none;
}
}

View File

@ -13,9 +13,19 @@ aside{
.thumbnail {
position: absolute;
top: 0;
height: 100%;
right: 0;
bottom: 0;
left: 0;
height: auto;
width: auto;
max-width: none;
z-index: -1;
opacity: 0.1;
opacity: 0.2;
.image {
padding-bottom: 0;
height: 100%;
}
}
nav {

View File

@ -34,8 +34,11 @@ main {
h3 {
font-size: 14px;
margin-bottom: 14px;
padding-bottom: 4px;
border-bottom: 1px solid #000000;
text-transform: uppercase;
&.underline {
padding-bottom: 4px;
border-bottom: 1px solid #000000;
}
}
}

10
src/scss/views/_artist.scss Executable file
View File

@ -0,0 +1,10 @@
.tracks {
width: 65%;
float: left;
}
.related-artist-list {
width: 30%;
float: right;
}