1 <!doctype html> 2 <!-- 3 Copyright 2013 The Polymer Authors. All rights reserved. 4 Use of this source code is governed by a BSD-style 5 license that can be found in the LICENSE file. 6 --> 7 <html> 8 <head> 9 <title>infinite-scroll</title> 10 <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> 11 <style> 12 html { 13 height: 100%; 14 } 15 body { 16 display: block; 17 height: 100%; 18 margin: 0; 19 } 20 #container { 21 overflow-y: scroll; 22 height: 100%; 23 margin: 0; 24 } 25 #runway { 26 height: 100000px; 27 } 28 .item { 29 -webkit-transform: translate3d(0,0,0); 30 box-sizing: border-box; 31 overflow: hidden; 32 } 33 34 .conversation { 35 font-family: sans-serif; 36 height: 70px; 37 display: flex; 38 border-bottom: 1px solid lightgray; 39 } 40 .avatar { 41 display: flex; 42 flex-shrink: 0; 43 color: white; 44 align-items: center; 45 justify-content: center; 46 font-size: 40px; 47 width: 50px; 48 height: 50px; 49 margin: 10px; 50 background-color: lightblue; 51 } 52 .summary { 53 flex: 1; 54 flex-shrink: 0; 55 display: flex; 56 flex-direction: column; 57 padding: 10px 10px 10px 0px; 58 } 59 .topline { 60 display: flex; 61 flex-shrink: 0; 62 } 63 .participants { 64 flex: 1; 65 flex-shrink: 0; 66 font-weight: bold; 67 } 68 .time { 69 flex-shrink: 0; 70 font-size: 12px; 71 color: #444; 72 } 73 .bottomline { 74 flex-shrink: 0; 75 display: flex; 76 } 77 .preview { 78 flex: 1; 79 flex-shrink: 0; 80 font-size: 12px; 81 height: 2em; 82 text-overflow: ellipsis; 83 } 84 .subject { 85 font-weight: bold; 86 } 87 .snippet { 88 color: #444; 89 } 90 .trinkets { 91 flex-shrink: 0; 92 font-size: 20px; 93 } 94 </style> 95 </head> 96 <body> 97 <div id="container"> 98 <div id="runway"> 99 <template> 100 <div class="item conversation"> 101 <div class="avatar">A</div> 102 <div class="summary"> 103 <div class="topline"> 104 <div class="participants">{{ participants }}</div> 105 <div class="time">{{ time }}</div> 106 </div> 107 <div class="bottomline"> 108 <div class="preview"><span class="subject">{{ subject }}</span> — 109 <span class="snippet">{{ snippet }}</span></div> 110 <div class="trinkets">☆</div> 111 </div> 112 </div> 113 </div> 114 </template> 115 </div> 116 </div> 117 <script> 118 function getRandomItem(array) { 119 return array[Math.floor(Math.random() * array.length)]; 120 } 121 function generateFakeData() { 122 var kNumberOfItems = 500; 123 var possibleParticipants = [ 124 'Adam', 'Ojan', 'Elliot', 'Chris', 125 ]; 126 var possibleTimes = [ 127 'Now', 'Yesterday', 'Last week', 128 ]; 129 var possibleSubjects = [ 130 'Do you even bench?', 131 'I like to scroll forever', 132 'Lunch', 133 'What if my subject is really long? Longer than that. Like really, really, long?', 134 ]; 135 var possibleSnippets = [ 136 'Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal.', 137 'When, in disgrace with fortune and men\'s eyes, I all alone beweep my outcast state,', 138 'We the People of the United States, in Order to form a more perfect Union, establish Justice, insure domestic Tranquility', 139 ]; 140 var data = new Array(kNumberOfItems); 141 for (var i = 0; i < kNumberOfItems; ++i) { 142 data[i] = { 143 participants: getRandomItem(possibleParticipants), 144 time: getRandomItem(possibleTimes), 145 subject: getRandomItem(possibleSubjects), 146 snippet: getRandomItem(possibleSnippets), 147 } 148 console.log(data[i]); 149 } 150 return data; 151 } 152 153 var data = generateFakeData(); 154 155 "use strict"; 156 (function(exports) { 157 var kHeight = 70; 158 var kPhysicalCount = 30; 159 160 var container = document.getElementById('container'); 161 var runway = document.getElementById('runway'); 162 163 var template = runway.querySelector('template'); 164 var items = new Array(kPhysicalCount); 165 for (var i = 0; i < kPhysicalCount; ++i) { 166 var fragment = template.content.cloneNode(true); 167 runway.appendChild(fragment); 168 var item = runway.lastElementChild; 169 items[i] = item; 170 item.transformValue_ = 0; 171 item.participants_ = item.querySelector('.participants').firstChild; 172 item.time_ = item.querySelector('.time').firstChild; 173 item.subject_ = item.querySelector('.subject').firstChild; 174 item.snippet_ = item.querySelector('.snippet').firstChild; 175 176 updateText(item, i); 177 } 178 179 var physicalHeight = kHeight * kPhysicalCount; 180 181 function updateText(item, index) { 182 var datum = data[index % data.length]; 183 item.participants_.nodeValue = datum.participants; 184 item.time_.nodeValue = datum.time; 185 item.subject_.nodeValue = datum.subject; 186 item.snippet_.nodeValue = datum.snippet; 187 } 188 189 container.addEventListener('scroll', function(e) { 190 var scrollTop = container.scrollTop; 191 192 var firstVirtualIndex = Math.floor(scrollTop / kHeight); 193 var firstPhysicalIndex = firstVirtualIndex % kPhysicalCount; 194 195 var baseVirtualIndex = firstVirtualIndex - firstPhysicalIndex; 196 197 var baseTransformValue = kHeight * baseVirtualIndex; 198 var nextTransformValue = baseTransformValue + physicalHeight; 199 200 var baseTransformString = 'translate3d(0,' + baseTransformValue + 'px,0)'; 201 var nextTransformString = 'translate3d(0,' + nextTransformValue + 'px,0)'; 202 203 window.requestAnimationFrame(function() { 204 for (var i = 0; i < firstPhysicalIndex; ++i) { 205 var item = items[i]; 206 if (item.transformValue_ != nextTransformValue) { 207 updateText(item, baseVirtualIndex + kPhysicalCount + i); 208 item.style.WebkitTransform = nextTransformString; 209 } 210 item.transformValue_ = nextTransformValue; 211 } 212 for (var i = firstPhysicalIndex; i < kPhysicalCount; ++i) { 213 var item = items[i]; 214 if (item.transformValue_ != baseTransformValue) { 215 updateText(item, baseVirtualIndex + i); 216 item.style.WebkitTransform = baseTransformString; 217 } 218 item.transformValue_ = baseTransformValue; 219 } 220 }); 221 }); 222 })(window); 223 </script> 224 </body> 225 </html> 226