Grids; Parallax
This commit is contained in:
@ -9,11 +9,11 @@ export default class AlbumGrid extends React.Component{
|
||||
}
|
||||
|
||||
render(){
|
||||
if( this.props.items ){
|
||||
if( this.props.albums ){
|
||||
return (
|
||||
<ul className="grid album-grid">
|
||||
{
|
||||
this.props.items.items.map(
|
||||
this.props.albums.map(
|
||||
(album, index) => {
|
||||
var link = album.uri;
|
||||
if( album.album ) link = album.album.uri;
|
||||
|
||||
@ -1,21 +1,22 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import AlbumGridItem from './AlbumGridItem'
|
||||
import GridItem from './GridItem'
|
||||
|
||||
export default class AlbumGrid extends React.Component{
|
||||
export default class ArtistGrid extends React.Component{
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render(){
|
||||
if( this.props.items ){
|
||||
if( this.props.artists ){
|
||||
return (
|
||||
<ul className="grid album-grid">
|
||||
<ul className="grid artist-grid">
|
||||
{
|
||||
this.props.items.items.map(
|
||||
(album, index) => {
|
||||
return <AlbumGridItem item={album} key={index} />
|
||||
this.props.artists.map(
|
||||
(artist, index) => {
|
||||
var link = artist.uri;
|
||||
return <GridItem item={artist} key={index} link={'/artist/'+link} />
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import FontAwesome from 'react-fontawesome'
|
||||
import { Link } from 'react-router'
|
||||
import Thumbnail from './Thumbnail'
|
||||
|
||||
export default class ArtistList extends React.Component{
|
||||
|
||||
@ -14,17 +14,12 @@ export default class ArtistList extends React.Component{
|
||||
<span className="artist-list">
|
||||
{
|
||||
this.props.artists.map( (artist, index) => {
|
||||
var separator = null;
|
||||
if( index == this.props.artists.length - 2 ){
|
||||
separator = ' and ';
|
||||
}else if( index < this.props.artists.length - 2 ){
|
||||
separator = ', ';
|
||||
}
|
||||
var link = '/artist/' + artist.uri;
|
||||
return (
|
||||
<span className="artist" key={artist.uri}>
|
||||
<Link to={ link }>{ artist.name }</Link>{ separator }
|
||||
</span>
|
||||
<Link to={ link } key={artist.uri} className="artist">
|
||||
<Thumbnail circle={true} size="small" images={artist.images} />
|
||||
<span className="name">{ artist.name }</span>
|
||||
</Link>
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
34
src/js/components/ArtistSentence.js
Executable file
34
src/js/components/ArtistSentence.js
Executable file
@ -0,0 +1,34 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import FontAwesome from 'react-fontawesome'
|
||||
import { Link } from 'react-router'
|
||||
|
||||
export default class ArtistSentence extends React.Component{
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render(){
|
||||
return (
|
||||
<span className="artist-sentence">
|
||||
{
|
||||
this.props.artists.map( (artist, index) => {
|
||||
var separator = null;
|
||||
if( index == this.props.artists.length - 2 ){
|
||||
separator = ' and ';
|
||||
}else if( index < this.props.artists.length - 2 ){
|
||||
separator = ', ';
|
||||
}
|
||||
var link = '/artist/' + artist.uri;
|
||||
return (
|
||||
<span className="artist" key={artist.uri}>
|
||||
<Link to={ link }>{ artist.name }</Link>{ separator }
|
||||
</span>
|
||||
);
|
||||
})
|
||||
}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,7 @@ import React, { PropTypes } from 'react'
|
||||
import { Link, browserHistory } from 'react-router'
|
||||
|
||||
import Thumbnail from './Thumbnail'
|
||||
import ArtistList from './ArtistList'
|
||||
import ArtistSentence from './ArtistSentence'
|
||||
|
||||
export default class GridItem extends React.Component{
|
||||
|
||||
@ -28,7 +28,7 @@ export default class GridItem extends React.Component{
|
||||
{ item.images ? <Thumbnail size="medium" images={item.images} /> : item.name }
|
||||
<div className="name">{ item.name }</div>
|
||||
<div className="secondary">
|
||||
{ item.artists ? <ArtistList artists={ item.artists } /> : null }
|
||||
{ item.artists ? <ArtistSentence artists={ item.artists } /> : null }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -8,25 +8,139 @@ export default class Parallax extends React.Component{
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
image: false
|
||||
scrollTop: 0,
|
||||
windowWidth: 0,
|
||||
windowHeight: 0,
|
||||
source: {},
|
||||
canvas: {
|
||||
width: 0,
|
||||
height: 0
|
||||
},
|
||||
image: {}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
this.setState({
|
||||
image: helpers.SizedImages( this.props.images )
|
||||
|
||||
this.loadImage( helpers.SizedImages( this.props.images ).huge )
|
||||
.then(
|
||||
response => this.setState({ image: response, source: {} })
|
||||
)
|
||||
|
||||
this.updateCanvas()
|
||||
$(window).resize(() => this.updateCanvas());
|
||||
}
|
||||
|
||||
loadImage( url ){
|
||||
return new Promise( (resolve, reject) => {
|
||||
|
||||
var imageObject = new Image();
|
||||
imageObject.src = url;
|
||||
|
||||
imageObject.onload = function(){
|
||||
resolve( imageObject )
|
||||
}
|
||||
})
|
||||
this.update();
|
||||
}
|
||||
|
||||
updateCanvas(){
|
||||
var canvasWidth = $('.parallax').outerWidth();
|
||||
var canvasHeight = $('.parallax').outerHeight();
|
||||
if( this.state.canvas.width != canvasWidth || this.state.canvas.height != canvasHeight ){
|
||||
this.setState({
|
||||
canvas: {
|
||||
width: canvasWidth,
|
||||
height: canvasHeight
|
||||
}
|
||||
})
|
||||
}
|
||||
console.log( this.state );
|
||||
}
|
||||
|
||||
update(){
|
||||
// TODO: copy canvas rules in here from Spotmop
|
||||
|
||||
let self = this;
|
||||
|
||||
var parallax = $('.parallax');
|
||||
var canvasDOM = document.getElementById('parallax-canvas');
|
||||
var context = canvasDOM.getContext('2d');
|
||||
|
||||
|
||||
var image = {};
|
||||
Object.assign(image, self.state.source);
|
||||
|
||||
// set our canvas dimensions (if necessary)
|
||||
var canvasWidth = $('#parallax-canvas').outerWidth();
|
||||
var canvasHeight = $('#parallax-canvas').outerHeight();
|
||||
if( context.canvas.width != canvasWidth || context.canvas.height != canvasHeight ){
|
||||
context.canvas.width = canvasWidth;
|
||||
context.canvas.height = canvasHeight;
|
||||
}
|
||||
|
||||
// zoom image to fill canvas, widthwise
|
||||
if( image.width < canvasWidth || image.width > canvasWidth ){
|
||||
var scale = canvasWidth / image.width;
|
||||
image.width = image.width * scale;
|
||||
image.height = image.height * scale;
|
||||
}
|
||||
|
||||
// now check for fill heightwise, and zoom in if necessary
|
||||
if( image.height < canvasHeight ){
|
||||
var scale = canvasHeight / image.height;
|
||||
image.width = image.width * scale;
|
||||
image.height = image.height * scale;
|
||||
}
|
||||
|
||||
// figure out where we want the image to be, based on scroll position
|
||||
var percent = Math.round( self.state.scrollTop / canvasHeight * 100 );
|
||||
var position = Math.round( (canvasHeight / 2) * (percent/100) ) - 100;
|
||||
|
||||
image.x = ( canvasWidth / 2 ) - ( image.width / 2 );
|
||||
image.y = ( ( canvasHeight / 2 ) - ( image.height / 2 ) ) + ( ( percent / 100 ) * 100);
|
||||
|
||||
// actually draw the image on the canvas
|
||||
context.drawImage(imageObject, image.x, image.y, image.width, image.height);
|
||||
|
||||
self.setState({ image: image });
|
||||
|
||||
// poll for scroll changes
|
||||
/*
|
||||
var animateInterval = $interval(
|
||||
function(){
|
||||
window.requestAnimationFrame(function( event ){
|
||||
|
||||
var bannerPanel = $(document).find('.intro');
|
||||
|
||||
// if we've scrolled or resized
|
||||
if(
|
||||
state.scrollTop != $(document).scrollTop() ||
|
||||
state.windowWidth != $(window).width() ||
|
||||
state.windowHeight != $(window).height() ){
|
||||
|
||||
// update our state
|
||||
state.scrollTop = $(document).scrollTop();
|
||||
state.windowWidth = $(window).width();
|
||||
state.windowHeight = $(window).height();
|
||||
|
||||
var bannerHeight = bannerPanel.outerHeight();
|
||||
|
||||
// and if we're within the bounds of our document
|
||||
// this helps prevent us animating when the objects in question are off-screen
|
||||
if( state.scrollTop < bannerHeight ){
|
||||
positionArtistBackground( image );
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
10
|
||||
);*/
|
||||
|
||||
}
|
||||
|
||||
render(){
|
||||
return (
|
||||
<div className="parallax">
|
||||
{ this.state.image ? <img src={this.state.image.huge} /> : null }
|
||||
<canvas id="parallax-canvas" width={this.state.canvas.width} height={this.state.canvas.height}></canvas>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ import { bindActionCreators } from 'redux'
|
||||
|
||||
import FontAwesome from 'react-fontawesome'
|
||||
import VolumeSlider from './VolumeSlider'
|
||||
import ArtistList from './ArtistList'
|
||||
import ArtistSentence from './ArtistSentence'
|
||||
import Thumbnail from './Thumbnail'
|
||||
|
||||
import * as mopidyActions from '../services/mopidy/actions'
|
||||
@ -22,7 +22,7 @@ class Player extends React.Component{
|
||||
<div className="track-in-focus">
|
||||
{ this.props.spotify.track && !this.props.mini ? <Thumbnail size="large" images={this.props.spotify.track.album.images} /> : null }
|
||||
<div className="title">{ this.props.mopidy.currentTlTrack.track.name }</div>
|
||||
<ArtistList artists={ this.props.mopidy.currentTlTrack.track.artists } />
|
||||
<ArtistSentence artists={ this.props.mopidy.currentTlTrack.track.artists } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import FontAwesome from 'react-fontawesome'
|
||||
import ArtistList from './ArtistList'
|
||||
import ArtistSentence from './ArtistSentence'
|
||||
import AlbumLink from './AlbumLink'
|
||||
|
||||
export default class Track extends React.Component{
|
||||
@ -58,7 +58,7 @@ export default class Track extends React.Component{
|
||||
{this.props.track.name}
|
||||
</span>
|
||||
<span className="col artists">
|
||||
{ this.props.track.artists ? <ArtistList artists={this.props.track.artists} /> : null }
|
||||
{ this.props.track.artists ? <ArtistSentence artists={this.props.track.artists} /> : null }
|
||||
</span>
|
||||
<span className="col album">
|
||||
{ this.props.track.album ? <AlbumLink album={this.props.track.album} /> : null }
|
||||
|
||||
@ -8,7 +8,7 @@ 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 ArtistList from '../components/ArtistList'
|
||||
|
||||
import * as spotifyActions from '../services/spotify/actions'
|
||||
|
||||
@ -34,13 +34,21 @@ class Artist extends React.Component{
|
||||
if( this.props.spotify.artist ){
|
||||
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 }
|
||||
<Parallax images={ this.props.spotify.artist.images } />
|
||||
<div className="intro">
|
||||
<Thumbnail size="huge" images={ this.props.spotify.artist.images } />
|
||||
<h1>{ this.props.spotify.artist.name }</h1>
|
||||
<p>{ this.props.spotify.artist.followers.total.toLocaleString() } followers</p>
|
||||
</div>
|
||||
<div className="col c3w2">
|
||||
<h3>Top tracks</h3>
|
||||
{ this.props.spotify.artist.tracks ? <TrackList tracks={ this.props.spotify.artist.tracks } /> : null }
|
||||
</div>
|
||||
<div className="col c3w1 cf">
|
||||
<h3>Related artists</h3>
|
||||
{ this.props.spotify.artist.related_artists ? <ArtistList artists={ this.props.spotify.artist.related_artists.slice(0,6) } /> : null }
|
||||
</div>
|
||||
{ this.props.spotify.artist_albums ? <AlbumGrid albums={ this.props.spotify.artist_albums.items } /> : null }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import FontAwesome from 'react-fontawesome'
|
||||
import TrackList from '../components/TrackList'
|
||||
import Track from '../components/Track'
|
||||
import Player from '../components/Player'
|
||||
import ArtistList from '../components/ArtistList'
|
||||
import ArtistSentence from '../components/ArtistSentence'
|
||||
import AlbumLink from '../components/AlbumLink'
|
||||
import Header from '../components/Header'
|
||||
|
||||
@ -24,7 +24,7 @@ class Queue extends React.Component{
|
||||
return (
|
||||
<div>
|
||||
<div>{ this.props.mopidy.trackInFocus.track.name }</div>
|
||||
<div><ArtistList artists={ this.props.mopidy.trackInFocus.track.artists } /></div>
|
||||
<div><ArtistSentence artists={ this.props.mopidy.trackInFocus.track.artists } /></div>
|
||||
<div><AlbumLink album={ this.props.mopidy.trackInFocus.track.album } /></div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -29,7 +29,7 @@ class LibraryAlbums extends React.Component{
|
||||
icon="cd"
|
||||
title="My albums"
|
||||
/>
|
||||
<AlbumGrid items={this.props.spotify.libraryAlbums} />
|
||||
<AlbumGrid albums={this.props.spotify.libraryAlbums.items} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import { bindActionCreators } from 'redux'
|
||||
import { Link } from 'react-router'
|
||||
|
||||
import Header from '../../components/Header'
|
||||
import ArtistGrid from '../../components/ArtistGrid'
|
||||
|
||||
import * as mopidyActions from '../../services/mopidy/actions'
|
||||
import * as spotifyActions from '../../services/spotify/actions'
|
||||
@ -28,14 +29,7 @@ class LibraryArtists extends React.Component{
|
||||
icon="mic"
|
||||
title="My artists"
|
||||
/>
|
||||
<ul>
|
||||
{
|
||||
this.props.spotify.libraryArtists.artists.items.map( (artist, index) => {
|
||||
var link = '/artist/' + artist.uri;
|
||||
return <li key={index}><Link to={link}>{ artist.name }</Link></li>
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
<ArtistGrid artists={this.props.spotify.libraryArtists.artists.items} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -25,8 +25,8 @@
|
||||
color: $secondary_grey;
|
||||
}
|
||||
|
||||
&:nth-child(5)::after {
|
||||
@include clear;
|
||||
&:nth-child(5) {
|
||||
@include clearfix;
|
||||
}
|
||||
|
||||
&:hover{
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
padding-bottom: 100%;
|
||||
position: relative;
|
||||
background-repeat: no-repeat;
|
||||
background-color: #DDDDDD;
|
||||
background-color: $light_grey;
|
||||
background-position: 50% 50%;
|
||||
background-size: cover;
|
||||
}
|
||||
@ -21,9 +21,13 @@
|
||||
}
|
||||
|
||||
.parallax {
|
||||
height: 300px;
|
||||
height: 50vh;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
.thumbnail {
|
||||
max-width: none;
|
||||
background: $light_grey;
|
||||
|
||||
canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
@ -14,6 +14,7 @@
|
||||
border-top: 1px solid #EEEEEE;
|
||||
border-bottom: 1px solid #EEEEEE;
|
||||
margin-bottom: -1px;
|
||||
@include clearfix;
|
||||
|
||||
.col {
|
||||
width: 10%;
|
||||
@ -21,10 +22,6 @@
|
||||
float: left;
|
||||
}
|
||||
|
||||
&::after {
|
||||
@include clear;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background: #FFF39C !important;
|
||||
}
|
||||
@ -79,3 +76,30 @@
|
||||
}
|
||||
}
|
||||
|
||||
.artist-list {
|
||||
.artist {
|
||||
display: block;
|
||||
border: 0;
|
||||
margin-bottom: 15px;
|
||||
@include clearfix;
|
||||
|
||||
.thumbnail {
|
||||
width: 50px;
|
||||
margin-right: 20px;
|
||||
float: left;
|
||||
border: 3px solid transparent;
|
||||
}
|
||||
|
||||
.name {
|
||||
display: inline-block;
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.thumbnail {
|
||||
border-color: $turquoise;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -87,7 +87,7 @@ aside{
|
||||
@include one_line_text;
|
||||
}
|
||||
|
||||
.artist-list {
|
||||
.artist-sentence {
|
||||
opacity: 0.5;
|
||||
display: block;
|
||||
@include one_line_text;
|
||||
|
||||
@ -41,4 +41,23 @@ main {
|
||||
border-bottom: 1px solid #000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.col {
|
||||
min-height: 1px;
|
||||
float: left;
|
||||
|
||||
&.c2w1 { width: 50%; }
|
||||
&.c3w1 { width: 33.3334%; }
|
||||
&.c3w2 { width: 66.6667%; }
|
||||
&.c4w1 { width: 25%; }
|
||||
&.c4w3 { width: 75%; }
|
||||
&.c5w1 { width: 20%; }
|
||||
&.c5w2 { width: 40%; }
|
||||
&.c5w3 { width: 60%; }
|
||||
&.c5w4 { width: 80%; }
|
||||
}
|
||||
|
||||
.cf{
|
||||
@include clearfix;
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
|
||||
$turquoise: #08d58f;
|
||||
$light_grey: #DDDDDD;
|
||||
$secondary_grey: #888888;
|
||||
|
||||
@mixin one_line_text {
|
||||
@ -8,8 +9,10 @@ $secondary_grey: #888888;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@mixin clear {
|
||||
content: '';
|
||||
clear: both;
|
||||
display: block;
|
||||
@mixin clearfix {
|
||||
&::after {
|
||||
content: '';
|
||||
clear: both;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@ -1,10 +1 @@
|
||||
|
||||
.tracks {
|
||||
width: 65%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.related-artist-list {
|
||||
width: 30%;
|
||||
float: right;
|
||||
}
|
||||
Reference in New Issue
Block a user