├── package.json
├── asset
├── css
│ ├── linechart.css
│ └── toggle.css
└── js
│ └── linechart.js
├── README.md
├── LICENSE
├── scrolly-slides.js
└── index.html
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "scrolly-slides",
3 | "version": "1.0.1",
4 | "description": "",
5 | "keywords": [
6 | "scroll",
7 | "slides",
8 | "scrollytelling"
9 | ],
10 | "author": "Mariko Kosaka",
11 | "license": "MIT"
12 | }
13 |
--------------------------------------------------------------------------------
/asset/css/linechart.css:
--------------------------------------------------------------------------------
1 | svg {
2 | font: 10px sans-serif;
3 | }
4 |
5 | .line {
6 | fill: none;
7 | stroke: hotpink;
8 | stroke-width: 4px;
9 | }
10 |
11 | .axis path,
12 | .axis line {
13 | fill: none;
14 | stroke: #000;
15 | shape-rendering: crispEdges;
16 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Scrolly Slide
2 | Presentation slides in "Scrollytelling" format
3 |
4 | ## Get Started
5 |
6 | ### Demo: http://kosamari.github.io/scrolly-slides/
7 |
8 | Also, take a look at `index.html` and `scrolly-slides.js` to see how it's built.
9 |
10 | ## Resources
11 |
12 | ### Learn more about Scrollytelling
13 |
14 | - [SO YOU THINK YOU CAN SCROLL](https://www.youtube.com/watch?v=fYQGgaE_b4I) by Jim Vallandingham
15 | - [graph-scroll.js](https://1wheel.github.io/graph-scroll/) by Adam Pearce
16 |
17 | ### Don't wanna scroll?
18 |
19 | - [SimpleSlides](https://github.com/jennschiffer/SimpleSlides) by Jenn Schiffer
20 |
21 | ## Changelog
22 | #### v1.1
23 |
24 | - fix opacity focus issue for very 1st element
25 |
26 | #### v1.0
27 |
28 | - the original commit
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016, Mariko Kosaka
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/asset/js/linechart.js:
--------------------------------------------------------------------------------
1 | var data = []
2 |
3 | var margin = {top: 20, right: 20, bottom: 20, left: 40},
4 | width = 500 - margin.left - margin.right,
5 | height = 400 - margin.top - margin.bottom
6 |
7 | var x = d3.scale.linear()
8 | .domain([0, 255])
9 | .range([0, width])
10 |
11 | var y = d3.scale.linear()
12 | .domain([0, 255])
13 | .range([height, 0])
14 |
15 | var line = d3.svg.line()
16 | .x(function(d, i) { return x(d.x) })
17 | .y(function(d, i) { return y(d.y) })
18 |
19 | var svg = d3.select('#chart').append('svg')
20 | .attr('width', width + margin.left + margin.right)
21 | .attr('height', height + margin.top + margin.bottom)
22 | .append('g')
23 | .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
24 |
25 | svg.append('g')
26 | .attr('class', 'x axis')
27 | .attr('transform', 'translate(0,' + y(0) + ')')
28 | .call(d3.svg.axis().scale(x).orient('bottom'))
29 |
30 | svg.append('g')
31 | .attr('class', 'y axis')
32 | .call(d3.svg.axis().scale(y).orient('left'))
33 |
34 |
35 | var path = svg.append('path')
36 | var drawn = false
37 | function draw() {
38 | drawn = true
39 | data = [{x:0,y:0},{x:64, y:64,},{x:192,y:192},{x:255,y:255}]
40 | path.datum(data)
41 | path.attr('class', 'line')
42 | .attr('d', line)
43 | }
44 |
45 | function update() {
46 | if(!drawn){
47 | draw()
48 | }
49 | data[1] = {x:64, y:255}
50 | data[2] = {x:192, y:0}
51 | path.transition()
52 | .duration(500)
53 | .ease('linear')
54 | .attr('d', line)
55 | }
56 |
57 |
58 | window.addEventListener('draw', draw)
59 | window.addEventListener('update', update)
--------------------------------------------------------------------------------
/asset/css/toggle.css:
--------------------------------------------------------------------------------
1 | /* ============================================================
2 | COMMON
3 | ============================================================ */
4 | .cmn-toggle {
5 | position: absolute;
6 | margin-left: -9999px;
7 | visibility: hidden;
8 | }
9 | .cmn-toggle + label {
10 | display: block;
11 | position: relative;
12 | cursor: pointer;
13 | outline: none;
14 | -webkit-user-select: none;
15 | -moz-user-select: none;
16 | -ms-user-select: none;
17 | user-select: none;
18 | }
19 |
20 | /* ============================================================
21 | SWITCH 2 - ROUND FLAT
22 | ============================================================ */
23 | input.cmn-toggle-round-flat + label {
24 | padding: 2px;
25 | width: 120px;
26 | height: 60px;
27 | background-color: #dddddd;
28 | -webkit-border-radius: 60px;
29 | -moz-border-radius: 60px;
30 | -ms-border-radius: 60px;
31 | -o-border-radius: 60px;
32 | border-radius: 60px;
33 | -webkit-transition: background 0.4s;
34 | -moz-transition: background 0.4s;
35 | -o-transition: background 0.4s;
36 | transition: background 0.4s;
37 | }
38 | input.cmn-toggle-round-flat + label:before, input.cmn-toggle-round-flat + label:after {
39 | display: block;
40 | position: absolute;
41 | content: "";
42 | }
43 | input.cmn-toggle-round-flat + label:before {
44 | top: 2px;
45 | left: 2px;
46 | bottom: 2px;
47 | right: 2px;
48 | background-color: #fff;
49 | -webkit-border-radius: 60px;
50 | -moz-border-radius: 60px;
51 | -ms-border-radius: 60px;
52 | -o-border-radius: 60px;
53 | border-radius: 60px;
54 | -webkit-transition: background 0.4s;
55 | -moz-transition: background 0.4s;
56 | -o-transition: background 0.4s;
57 | transition: background 0.4s;
58 | }
59 | input.cmn-toggle-round-flat + label:after {
60 | top: 4px;
61 | left: 4px;
62 | bottom: 4px;
63 | width: 52px;
64 | background-color: #dddddd;
65 | -webkit-border-radius: 52px;
66 | -moz-border-radius: 52px;
67 | -ms-border-radius: 52px;
68 | -o-border-radius: 52px;
69 | border-radius: 52px;
70 | -webkit-transition: margin 0.4s, background 0.4s;
71 | -moz-transition: margin 0.4s, background 0.4s;
72 | -o-transition: margin 0.4s, background 0.4s;
73 | transition: margin 0.4s, background 0.4s;
74 | }
75 | input.cmn-toggle-round-flat:checked + label {
76 | background-color: #8ce196;
77 | }
78 | input.cmn-toggle-round-flat:checked + label:after {
79 | margin-left: 60px;
80 | background-color: #8ce196;
81 | }
82 | .switch {
83 | display: table-cell;
84 | vertical-align: middle;
85 | padding: 10px;
86 | }
--------------------------------------------------------------------------------
/scrolly-slides.js:
--------------------------------------------------------------------------------
1 | function Scrolly () {
2 | var articleWidth = 340
3 | var sectionPositions = []
4 | var currentSection = 0
5 | var $el = {
6 | window: $(window),
7 | article: $('.scrolly-article'),
8 | slides: $('.scrolly-slide'),
9 | sections: $('.scrolly-section')
10 | }
11 |
12 | /*
13 | * UTILITIES
14 | */
15 |
16 | function findClosest (array, target) {
17 | return array.map(function (val, i) {
18 | return [val, i, Math.abs(val - target)]
19 | }).reduce(function (memo, val) {
20 | return (memo[2] < val[2]) ? memo : val
21 | })
22 | }
23 |
24 | // Scrolly Slide adds page number hash
25 | function readURL () {
26 | var id = window.location.hash.split('#')[1]
27 | return id ? parseInt(id, 10) : 0
28 | }
29 |
30 | function writeURL (loc) {
31 | window.location.hash = loc
32 | }
33 |
34 | /*
35 | * EVENT HANDLERS
36 | */
37 |
38 | function handleWindowResize () {
39 | // resize width of slide elements
40 | var margin = parseInt($el.slides.css('margin-left'), 10)
41 | var paddingL = parseInt($el.slides.css('padding-left'), 10)
42 | var paddingR = parseInt($el.slides.css('padding-right'), 10)
43 |
44 | if (margin) { // article view is visible
45 | $el.slides.css({width: window.innerWidth - margin - paddingL - paddingR})
46 | } else {
47 | $el.slides.css({width: window.innerWidth - paddingL - paddingR})
48 | }
49 |
50 | // add bottom margin to the last section
51 | var $lastel = $('.scrolly-section-last')
52 | var marginBottom = window.innerHeight - ($lastel[0].getBoundingClientRect().bottom - $lastel[0].getBoundingClientRect().top)
53 |
54 | $lastel.css({'margin-bottom': marginBottom + 'px'})
55 | }
56 |
57 | function handleScroll (newSectionIndex) {
58 | // get data of current section from data attributes
59 | var $sctionEl = $($el.sections[newSectionIndex])
60 | var slideId = $sctionEl.data('slideId')
61 | var fragmentIds = $sctionEl.data('fragmentIds')
62 | var eventName = $sctionEl.data('eventName')
63 |
64 | // transform fragmentIds to an Array
65 | if (fragmentIds) {
66 | fragmentIds = fragmentIds.toString().split(',').map(function (e) { return parseInt(e, 10) })
67 | }
68 |
69 | // update current section counter
70 | currentSection = newSectionIndex
71 |
72 | // change page hash
73 | writeURL(newSectionIndex)
74 |
75 | // update opacity of sections (current section is set to opacity 1)
76 | $el.sections.each(function (index, sectionEl) {
77 | var el = $(sectionEl)
78 | if (index === currentSection) {
79 | return el.css({'opacity': 1})
80 | }
81 | return el.css({'opacity': 0.2})
82 | })
83 |
84 | // update each slide element based on data attributes of section element
85 | $el.slides.each(function (index, slideEl) {
86 | var el = $(slideEl)
87 |
88 | // Set visiblity of slide element
89 | if(slideId === el.data('slideId')){
90 | // trigger Event
91 | if (eventName) {
92 | window.dispatchEvent(new Event(eventName))
93 | }
94 |
95 | // check fragments to render
96 | if (fragmentIds) {
97 | el.children().each(function (index, cel) {
98 | var $cel = $(cel)
99 | fragmentIds.indexOf($cel.data('fragmentId')) >= 0
100 | ? $cel.show()
101 | : $cel.hide()
102 | })
103 | }
104 | return el.show()
105 | }
106 | return el.hide()
107 | })
108 | }
109 |
110 | /*
111 | * PUBLIC METHODS
112 | */
113 |
114 | // Toggle article view
115 | this.toggleArticle = function (show) {
116 | var width = show ? articleWidth : 0
117 | var paddingL = parseInt($el.slides.css('padding-left'), 10)
118 | var paddingR = parseInt($el.slides.css('padding-right'), 10)
119 | $el.slides.animate({
120 | 'margin-left': width + 'px',
121 | 'width': (window.innerWidth - width - paddingL - paddingR) + 'px'
122 | })
123 | }
124 |
125 | // init method
126 | this.init = function (opt) {
127 | opt = opt || {}
128 |
129 | // set width of article
130 | $el.article.css({width: articleWidth + 'px'})
131 |
132 | // show/hide article based on option passed
133 | var width = opt.show_article ? articleWidth : 0
134 | $el.slides.css({'margin-left': width + 'px'})
135 |
136 | // create position map of sections
137 | var startPos
138 | $el.sections.each(function (i, el) {
139 | var top = el.getBoundingClientRect().top
140 | if (i === 0) {
141 | startPos = top
142 | }
143 | sectionPositions.push(top - startPos)
144 | })
145 |
146 | // hide everything except very 1st .scrolly-slide element
147 | $el.slides.each(function (index, slideEl) {
148 | if (index) {
149 | $(slideEl).hide()
150 | }
151 | })
152 |
153 | // bind window resize event
154 | handleWindowResize()
155 | $el.window.on('resize', function () {
156 | handleWindowResize()
157 | })
158 |
159 |
160 | // bind scroll event
161 | $el.window.scroll(function () {
162 | var position = window.pageYOffset - 10
163 | var newSectionIndex = findClosest(sectionPositions, position)[1]
164 | if (currentSection !== newSectionIndex) {
165 | handleScroll(newSectionIndex)
166 | }
167 | })
168 |
169 | // bind key event
170 | $el.window.keydown(function (e) {
171 | var prev = currentSection - 1
172 | var next = currentSection + 1
173 | switch (e.keyCode) {
174 | case 39: // right arrow
175 | e.preventDefault()
176 | if (next < sectionPositions.length) {
177 | window.scrollTo(0, sectionPositions[next])
178 | }
179 | break
180 | case 40: // down arrow
181 | e.preventDefault()
182 | if (next < sectionPositions.length) {
183 | window.scrollTo(0, sectionPositions[next])
184 | }
185 | break
186 | case 34: // page down
187 | e.preventDefault()
188 | if (next < sectionPositions.length) {
189 | window.scrollTo(0, sectionPositions[next])
190 | }
191 | break
192 | case 32: // space
193 | e.preventDefault()
194 | if (next < sectionPositions.length) {
195 | window.scrollTo(0, sectionPositions[next])
196 | }
197 | break
198 | case 37: // left arrow
199 | e.preventDefault()
200 | if (prev >= 0) {
201 | window.scrollTo(0, sectionPositions[prev])
202 | }
203 | break
204 | case 38: // up arrow
205 | e.preventDefault()
206 | if (prev >= 0) {
207 | window.scrollTo(0, sectionPositions[prev])
208 | }
209 | break
210 | case 33: // page up
211 | e.preventDefault()
212 | if (prev >= 0) {
213 | window.scrollTo(0, sectionPositions[prev])
214 | }
215 | break
216 | default: return
217 | }
218 | })
219 |
220 | // Lastly, look up page hash & scroll to appropriate location
221 | var url = readURL()
222 | if (!url) { handleScroll(0) }
223 | window.scrollTo(0, sectionPositions[readURL()])
224 | }
225 | }
226 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Scrolly Slides
6 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
90 |
91 |
92 |
98 |
99 |
100 |
109 |
110 |
111 |
115 |
116 |
119 |
120 |
123 |
124 |
125 |
130 |
131 |
135 |
136 |
137 |
145 |
146 |
147 |
148 |
149 |
153 |
154 |
164 |
165 |
166 |
177 |
178 |
179 |
199 |
200 |
201 |
206 |
207 |
208 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
231 |
232 |
--------------------------------------------------------------------------------