Frame-rate limiter for m.mount/m.route

This commit is contained in:
Pat Cavit 2016-05-17 14:49:48 -07:00
parent 5ec06e7c08
commit db609b9142
5 changed files with 147 additions and 13 deletions

35
limiter.js Normal file
View file

@ -0,0 +1,35 @@
var FRAME_BUDGET = 16 // 60 frames per second = 1 call per 16 ms
module.exports = function($window, render) {
var rAF = $window.requestAnimationFrame || $window.setTimeout
var cAF = $window.cancelAnimationFrame || $window.clearTimeout
var last = 0
var pending
return function() {
var now = new Date()
// First render, OR if the time since the last render is greater
// than the frame budget
// just immediately render
if(!last || now - last > FRAME_BUDGET) {
last = now;
return render()
}
// Redraw already pending, abort
if(pending) {
return
}
// Schedule a redraw for the next tick
pending = rAF(function() {
render()
last = new Date()
pending = null
}, FRAME_BUDGET - (now - last))
}
}

View file

@ -575,21 +575,58 @@ var createRenderer = function($window) {
return {render: render, setEventCallback: setEventCallback} return {render: render, setEventCallback: setEventCallback}
} }
var FRAME_BUDGET = 16 // 60 frames per second = 1 call per 16 ms
var limiter = function($window, render) {
var rAF = $window.requestAnimationFrame || $window.setTimeout
var cAF = $window.cancelAnimationFrame || $window.clearTimeout
var last = 0
var pending
return function() {
var now = new Date()
// First render, OR if the time since the last render is greater
// than the frame budget
// just immediately render
if(!last || now - last > FRAME_BUDGET) {
last = now;
return render()
}
// Redraw already pending, abort
if(pending) {
return
}
// Schedule a redraw for the next tick
pending = rAF(function() {
render()
last = new Date()
pending = null
}, FRAME_BUDGET - (now - last))
}
}
;
var createMounter = function($window, redraw) { var createMounter = function($window, redraw) {
return function(root, component) { return function(root, component) {
var renderer = createRenderer($window) var renderer = createRenderer($window)
renderer.setEventCallback(draw) var draw = limiter($window, function draw() {
function draw() {
renderer.render(root, {tag: component}) renderer.render(root, {tag: component})
} })
renderer.setEventCallback(draw)
redraw.run = draw redraw.run = draw
draw() draw()
} }
} }
var buildQueryString = function buildQueryString(object) { var buildQueryString = function buildQueryString(object) {
if (Object.prototype.toString.call(object) !== "[object Object]") return "" if (Object.prototype.toString.call(object) !== "[object Object]") return ""
@ -758,15 +795,17 @@ var createRouter = function($window) {
return {setPrefix: setPrefix, getPath: getPath, setPath: setPath, defineRoutes: defineRoutes, link: link} return {setPrefix: setPrefix, getPath: getPath, setPath: setPath, defineRoutes: defineRoutes, link: link}
} }
var createRouterInstance = function($window, redraw) { var createRouterInstance = function($window, redraw) {
var renderer = createRenderer($window) var renderer = createRenderer($window)
var router = createRouter($window) var router = createRouter($window)
var route = function(root, defaultRoute, routes) { var route = function(root, defaultRoute, routes) {
var replay = router.defineRoutes(routes, function(component, args) { var replay = limiter($window, router.defineRoutes(routes, function(component, args) {
renderer.render(root, {tag: component, attrs: args}) renderer.render(root, {tag: component, attrs: args})
}, function() { }, function() {
router.setPath(defaultRoute) router.setPath(defaultRoute)
}) }))
renderer.setEventCallback(replay) renderer.setEventCallback(replay)
redraw.run = replay redraw.run = replay
} }

View file

@ -1,15 +1,16 @@
var createRenderer = require("./render/render") var createRenderer = require("./render/render")
var limiter = require("./limiter");
module.exports = function($window, redraw) { module.exports = function($window, redraw) {
return function(root, component) { return function(root, component) {
var renderer = createRenderer($window) var renderer = createRenderer($window)
renderer.setEventCallback(draw) var draw = limiter($window, function draw() {
function draw() {
renderer.render(root, {tag: component}) renderer.render(root, {tag: component})
} })
renderer.setEventCallback(draw)
redraw.run = draw redraw.run = draw
draw() draw()
} }
} }

View file

@ -1,15 +1,17 @@
var createRenderer = require("./render/render") var createRenderer = require("./render/render")
var createRouter = require("./router/router") var createRouter = require("./router/router")
var limiter = require("./limiter")
module.exports = function($window, redraw) { module.exports = function($window, redraw) {
var renderer = createRenderer($window) var renderer = createRenderer($window)
var router = createRouter($window) var router = createRouter($window)
var route = function(root, defaultRoute, routes) { var route = function(root, defaultRoute, routes) {
var replay = router.defineRoutes(routes, function(component, args) { var replay = limiter($window, router.defineRoutes(routes, function(component, args) {
renderer.render(root, {tag: component, attrs: args}) renderer.render(root, {tag: component, attrs: args})
}, function() { }, function() {
router.setPath(defaultRoute) router.setPath(defaultRoute)
}) }))
renderer.setEventCallback(replay) renderer.setEventCallback(replay)
redraw.run = replay redraw.run = replay
} }

57
test-redraw.html Normal file
View file

@ -0,0 +1,57 @@
<!doctype html>
<html>
<head></head>
<style>
html, body {
width: 100%;
height: 100%;
margin: 0;
}
.mount {
display: flex;
flex-direction: column;
height: 100%;
}
.outer {
flex: 1;
overflow-y: auto;
}
.inner {
height: 99999px;
background: #CCC;
}
</style>
<body>
<div class="mount"></div>
<script src="./module/module.js"></script>
<script src="./mithril.js"></script>
<script type="text/javascript">
var m = require("./mithril")
var last = 0
m.mount(document.querySelector(".mount"), {
view : function() {
var now = performance.now()
console.log("draw", now - last)
last = now
return m(".outer", {
onscroll : function() {
// Unused except to trigger redraws
}
},
m(".inner",
m("p", "Scroll me")
)
)
}
})
</script>
</body>
</html>