Skip to content

Instantly share code, notes, and snippets.

@GerHobbelt
Created September 16, 2012 15:41
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save GerHobbelt/3732893 to your computer and use it in GitHub Desktop.
Save GerHobbelt/3732893 to your computer and use it in GitHub Desktop.
d3.js: mouse drag a la iphone: decceleration
{
"tip_of_the_day": [
{
"title": "Introduction",
"description": [
"A huge image is shown on screen; you can pan/zoom the image to see all the detail or the entire image at once.",
"Each of the possible operations can be performed by one of several mouse actions, some operations can also be performed using the keyboard."
]
},
{
"title": "When all is quiet...",
"description": ["When you have been 'inactive' for 5 seconds or more, i.e. have not moved the mouse or pressed a key, then these 'tips' will show up and cycle, telling you what can be done."]
},
{
"title": "Action: Swipe",
"description": ["Click and drag the image (background), release during drag, i.e. act it out as a swipe movement."]
},
{
"title": "Action: Pan",
"description": [
"Either swipe (drag and release mouse button while still moving) or drag the image and hold in place, then release, to pan the image around.",
"You may also move the mouse near the 'edges' to auto-pan."
],
"keys": ["pgup", "pgdown", "up", "down"]
},
{
"title": "Action: Center",
"description": ["Click somewhere to center on that pixel."]
},
{
"title": "Action: Zoom",
"description": [
"Zooming in/out:",
"- Scrollwheel",
"- CTRL+drag (right/up is zoom in, left/down is zoom out)"
],
"keys": ["+", "-"]
},
{
"title": "Action: Reset",
"description": ["Right click."],
"keys": ["end", "E"]
}
]
}

User Interface

A huge image is shown on screen; you can pan/zoom the image to see all the detail or the entire image at once.

Each of the possible operations can be performed by one of several mouse actions. Some operations have only one type of mouse activity.

When the operation can be performed using the keyboard, then the key bindings are listed as well.

Swipe

Click and drag the image (background), release during drag, i.e. act it out as a swipe movement.

keys: ---

Pan

Either swipe (see above) or drag the image (background) and hold in place, then release, to pan the image around.

You may also move the mouse near the 'edges' to auto-pan.

keys: [Page Up], [Page Down], [Arrow Up], [Arrow Down]

Centering

Click somewhere to center on that pixel.

keys: ---

Zoom

Zooming in/out:

  • Scrollwheel
  • CTRL+drag (right/up is zoom in, left/down is zoom out)

keys: [+], [-], [Keypad +], [Keypad -]

Reset

Right click.

keys: [END], [E]

TODO

Create and test mobile/tablet actions for each of the operations.

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script type="text/javascript" src="../../html/js/d3/d3.latest.js"></script>
<script type="text/javascript" src="../../html/js/jwerty/jwerty.js"></script>
<link href='http://fonts.googleapis.com/css?family=Abel' rel='stylesheet' type='text/css'>
<style type="text/css">
body
{
font-family: 'Abel', sans-serif;
font-size: 14px;
}
h1
{
font-size: 200%;
font-weight: normal;
}
#chart
{
position: relative;
text-align: center;
margin: 0 auto;
height: 460px;
width: 920px;
}
#manipulated-area
{
position: relative;
height: 460px;
width: 920px;
overflow: hidden;
}
#progress
{
position: absolute;
top: 45%;
left: 50%;
}
#konachan
{
position: absolute;
top: -500px;
left: -12000px;
opacity: 0.2;
}
#tip_of_the_day
{
background: black;
border: solid white 3px;
border-radius: 10px;
box-shadow: 0 0 0 3px black, 2px 2px 6px 3px black;
padding: 6px 24px;
color: white;
width: 600px;
display: none;
position: absolute;
top: 30%;
left: 15%;
}
#tip_of_the_day h1
{
text-align: center;
}
</style>
</head>
<body>
<div id="chart">
<div id="manipulated-area">
<img id="progress" src="images/progress.gif" >
<img id="konachan" src="http://hebbut.net/gist-3732893/Konachan.com-82511.akiyama_mio.cirno.crossover.flcl.hanyuu.ikari_gendo.k-on!.parody.porco_rosso.rakka.reki.rozen_maiden.souseiseki.suigintou.takano_miyo.touhou.jpg" >
</div>
<div id="tip_of_the_day">
<h1>title</h1>
<p>bla bla bla bla</p>
</div>
</div>
<script type="text/javascript">
var viewarea = d3.select("#chart");
var img = viewarea.select("#konachan");
/*
{
"title": <string>,
"description": [ ... ],
"keys": [...]
}
*/
function show_tip(tip, duration) {
var tip_box = d3.select("#tip_of_the_day");
tip_box
.style("opacity", 1e-6)
.style("display", "block")
.transition()
.duration(1000)
.style("opacity", 1.0)
.transition()
.delay(duration)
.duration(1000)
.style("opacity", 1e-6)
// only once we're COMPLETELY TRANSPARENT, do we 'hide' the node:
.each("end", function(d) {
d3.select(this)
.style("display", "none");
});
tip_box.select("h1")
.text(tip.title);
var p = tip_box.selectAll("p").data(tip.description)
.text(function(d) {
return d;
});
p
.exit().remove();
p
.enter().append("p").text(function(d) {
return d;
});
}
function hide_tip(tip) {
var tip_box = d3.select("#tip_of_the_day");
// this transition nukes all the previous registered/running ones for this selection ~ DOM node set.
tip_box
.transition()
.style("opacity", 1e-6)
// only once we're COMPLETELY TRANSPARENT, do we 'hide' the node:
.each("end", function(d) {
d3.select(this)
.style("display", "none");
});
}
d3.json("README.json", function(json) {
var tip = null;
var tip_index = 0;
var duration = 1000;
var inactivity_action = function() {
tip_index %= json.tip_of_the_day.length;
tip = json.tip_of_the_day[tip_index++];
duration = 0;
tip.description.forEach(function(d) {
duration += d.length;
});
duration *= 1000 / 16; // reading speed estimate (the lower bound): 16 characters per second
show_tip(tip, duration);
duration += 3000; // 3 seconds between tips
d3.timer(inactivity_action, duration);
// return true; // stop timer from calling us again based on the original d3.timer setup call
return false; // make sure our callback, now with new timeout, will be triggered
};
// show first tip after 1 second
d3.timer(inactivity_action, duration);
d3.select("body")
.on("mousemove", function() {
if (tip) {
hide_tip(tip);
tip = null;
}
d3.timer(inactivity_action, duration = 5000);
});
var pan_behaviour = d3.behavior.drag()
.origin(function() {
return this; /* DOM node already delivers .x and .y by itself */
})
.on("dragstart", function() {
console.log(d3.event);
})
.on("drag", function() {
console.log(d3.event);
})
.on("dragend", function() {
console.log(d3.event);
});
img.call(pan_behaviour)
.on("mousemove.monitor", function() {
console.log(d3.event);
});
img.on("mousemove", function() {
});
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment