Add touch targets for line/area

This commit is contained in:
Bryan Housel
2017-12-17 22:53:58 -05:00
parent 5e99f3cbac
commit aa68b21d7a
11 changed files with 132 additions and 55 deletions

View File

@@ -14,17 +14,24 @@
pointer-events: none;
}
/* Line/Area shadows and point/vertex targets are interactive */
/* `.target` objects are interactive */
/* They can be picked up, clicked, hovered, or things can connect to them */
.layer-areas path.shadow,
.layer-lines path.shadow {
pointer-events: stroke;
}
.layer-points-targets * {
pointer-events: all;
.node.target {
pointer-events: fill;
fill-opacity: 0.8;
fill: currentColor;
stroke: none;
}
/* .active objects (currently being drawn or dragged) are not interactive */
.way.target {
pointer-events: stroke;
fill: none;
stroke-width: 10;
stroke-opacity: 0.8;
stroke: currentColor;
}
/* `.active` objects (currently being drawn or dragged) are not interactive */
/* This is important to allow the events to drop through to whatever is */
/* below them on the map, so you can still hover and connect to other things. */
.layer-osm .active {
@@ -95,12 +102,6 @@ g.midpoint .shadow {
fill-opacity: 0;
}
.target {
color: rgba(0,0,0,0);
fill-opacity: 0.8;
fill: currentColor;
}
g.vertex.related:not(.selected) .shadow,
g.vertex.hover:not(.selected) .shadow,
g.midpoint.related:not(.selected) .shadow,

View File

@@ -33,4 +33,5 @@
.fill-partial path.area.fill {
fill-opacity: 0;
stroke-width: 60px;
pointer-events: visibleStroke;
}

View File

@@ -2864,6 +2864,7 @@ img.tile-removing {
stroke-width: 1;
}
.nocolor { color: rgba(0, 0, 0, 0); }
.red { color: rgba(255, 0, 0, 0.75); }
.green { color: rgba(0, 255, 0, 0.75); }
.blue { color: rgba(0, 0, 255, 0.75); }

View File

@@ -116,6 +116,9 @@ export function behaviorDraw(context) {
function click() {
var d = datum();
// Try to snap..
// See also: `modes/drag_node.js doMove()`
if (d.type === 'way') {
var dims = context.map().dimensions();
var mouse = context.mouse();

View File

@@ -131,8 +131,8 @@ export function modeDragNode(context) {
_dragEntity = entity;
// activeIDs generate no pointer events. This prevents the node or vertex
// being dragged from trying to connect to itself or its parent element.
// `.active` elements have `pointer-events: none`.
// This prevents the node or vertex being dragged from trying to connect to itself.
_activeIDs = context.graph().parentWays(entity)
.map(function(parent) { return parent.id; });
_activeIDs.push(entity.id);
@@ -158,17 +158,25 @@ export function modeDragNode(context) {
var currPoint = (d3_event && d3_event.point) || context.projection(_lastLoc);
var currMouse = vecSub(currPoint, nudge);
var loc = context.projection.invert(currMouse);
var d = datum();
if (!_nudgeInterval) {
// try to snap
// If we're not nudging at the edge of the viewport, try to snap..
// See also `behavior/draw.js click()`
var d = datum();
// Snap to a node (not self)
if (d.type === 'node' && d.id !== entity.id) {
loc = d.loc;
// Snap to a way (not an area fill)
} else if (d.type === 'way' && !d3_select(d3_event.sourceEvent.target).classed('fill')) {
var childNodes = context.childNodes(d);
var childIDs = childNodes.map(function(node) { return node.id; });
if (childIDs.indexOf(entity.id) === -1) {
loc = geoChooseEdge(childNodes, context.mouse(), context.projection).loc;
// var childNodes = context.childNodes(d);
// var childIDs = childNodes.map(function(node) { return node.id; });
var choice = geoChooseEdge(context.childNodes(d), context.mouse(), context.projection);
// (not along a segment adjacent to self)
if (entity.id !== d.nodes[choice.index - 1] && entity.id !== d.nodes[choice.index]) {
loc = choice.loc;
}
}
}

View File

@@ -41,7 +41,29 @@ export function svgAreas(projection, context) {
}
return function drawAreas(selection, graph, entities, filter) {
function drawTargets(selection, graph, entities, filter) {
var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
var getPath = svgPath(projection, graph);
var targets = selection.selectAll('.area.target')
.filter(filter)
.data(entities, function key(d) { return d.id; });
// exit
targets.exit()
.remove();
// enter/update
targets.enter()
.append('path')
.merge(targets)
.attr('d', getPath)
.attr('class', function(d) { return 'way area target ' + fillClass + d.id; });
}
function drawAreas(selection, graph, entities, filter) {
var path = svgPath(projection, graph, true),
areas = {},
multipolygon;
@@ -99,7 +121,7 @@ export function svgAreas(projection, context) {
.attr('d', path);
var layer = selection.selectAll('.layer-areas');
var layer = selection.selectAll('.layer-areas .layer-areas-areas');
var areagroup = layer
.selectAll('g.areagroup')
@@ -145,5 +167,12 @@ export function svgAreas(projection, context) {
})
.call(svgTagClasses())
.attr('d', path);
};
// touch targets
selection.selectAll('.layer-areas .layer-areas-targets')
.call(drawTargets, graph, data.stroke, filter);
}
return drawAreas;
}

View File

@@ -36,13 +36,33 @@ export function svgLines(projection, context) {
};
function drawTargets(selection, graph, entities, filter) {
var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
var getPath = svgPath(projection, graph);
var targets = selection.selectAll('.line.target')
.filter(filter)
.data(entities, function key(d) { return d.id; });
// exit
targets.exit()
.remove();
// enter/update
targets.enter()
.append('path')
.merge(targets)
.attr('d', getPath)
.attr('class', function(d) { return 'way line target ' + fillClass + d.id; });
}
function drawLines(selection, graph, entities, filter) {
function waystack(a, b) {
var selected = context.selectedIDs(),
scoreA = selected.indexOf(a.id) !== -1 ? 20 : 0,
scoreB = selected.indexOf(b.id) !== -1 ? 20 : 0;
var selected = context.selectedIDs();
var scoreA = selected.indexOf(a.id) !== -1 ? 20 : 0;
var scoreB = selected.indexOf(b.id) !== -1 ? 20 : 0;
if (a.tags.highway) { scoreA -= highway_stack[a.tags.highway]; }
if (b.tags.highway) { scoreB -= highway_stack[b.tags.highway]; }
@@ -91,15 +111,15 @@ export function svgLines(projection, context) {
}
var getPath = svgPath(projection, graph),
ways = [],
pathdata = {},
onewaydata = {},
oldMultiPolygonOuters = {};
var getPath = svgPath(projection, graph);
var ways = [];
var pathdata = {};
var onewaydata = {};
var oldMultiPolygonOuters = {};
for (var i = 0; i < entities.length; i++) {
var entity = entities[i],
outer = osmSimpleMultipolygonOuterMember(entity, graph);
var entity = entities[i];
var outer = osmSimpleMultipolygonOuterMember(entity, graph);
if (outer) {
ways.push(entity.mergeTags(outer.tags));
oldMultiPolygonOuters[outer.id] = true;
@@ -117,7 +137,7 @@ export function svgLines(projection, context) {
});
var layer = selection.selectAll('.layer-lines');
var layer = selection.selectAll('.layer-lines .layer-lines-lines');
var layergroup = layer
.selectAll('g.layergroup')
@@ -164,8 +184,8 @@ export function svgLines(projection, context) {
.selectAll('path')
.filter(filter)
.data(
function() { return onewaydata[this.parentNode.__data__] || []; },
function(d) { return [d.id, d.index]; }
function data() { return onewaydata[this.parentNode.__data__] || []; },
function key(d) { return [d.id, d.index]; }
);
oneways.exit()
@@ -181,6 +201,11 @@ export function svgLines(projection, context) {
if (detected.ie) {
oneways.each(function() { this.parentNode.insertBefore(this, this); });
}
// touch targets
selection.selectAll('.layer-lines .layer-lines-targets')
.call(drawTargets, graph, ways, filter);
}

View File

@@ -17,7 +17,7 @@ export function svgMidpoints(projection, context) {
function drawTargets(selection, graph, entities, filter) {
var debugClass = 'pink';
var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
var targets = selection.selectAll('.midpoint.target')
.filter(filter)
.data(entities, function key(d) { return d.id; });
@@ -30,10 +30,9 @@ export function svgMidpoints(projection, context) {
targets.enter()
.append('circle')
.attr('r', 12)
.attr('class', function(d) { return 'midpoint target ' + d.id; })
.merge(targets)
.attr('transform', svgPointTransform(projection))
.classed(debugClass, context.getDebug('target'));
.attr('class', function(d) { return 'node midpoint target ' + fillClass + d.id; })
.attr('transform', svgPointTransform(projection));
}

View File

@@ -9,6 +9,18 @@ export function svgOsm(projection, context, dispatch) {
.append('g')
.attr('class', function(d) { return 'layer-osm layer-' + d; });
selection.selectAll('.layer-areas').selectAll('.layer-areas-group')
.data(['areas', 'targets'])
.enter()
.append('g')
.attr('class', function(d) { return 'layer-areas-group layer-areas-' + d; });
selection.selectAll('.layer-lines').selectAll('.layer-lines-group')
.data(['lines', 'targets'])
.enter()
.append('g')
.attr('class', function(d) { return 'layer-lines-group layer-lines-' + d; });
selection.selectAll('.layer-points').selectAll('.layer-points-group')
.data(['points', 'midpoints', 'vertices', 'turns', 'targets'])
.enter()

View File

@@ -22,7 +22,7 @@ export function svgPoints(projection, context) {
function drawTargets(selection, graph, entities, filter) {
var debugClass = 'pink';
var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
var targets = selection.selectAll('.point.target')
.filter(filter)
.data(entities, function key(d) { return d.id; });
@@ -34,14 +34,13 @@ export function svgPoints(projection, context) {
// enter/update
targets.enter()
.append('rect')
.attr('x', -15)
.attr('y', -30)
.attr('width', 30)
.attr('height', 36)
.attr('class', function(d) { return 'node point target ' + d.id; })
.attr('x', -10)
.attr('y', -26)
.attr('width', 20)
.attr('height', 30)
.merge(targets)
.attr('transform', svgPointTransform(projection))
.classed(debugClass, context.getDebug('target'));
.attr('class', function(d) { return 'node point target ' + fillClass + d.id; })
.attr('transform', svgPointTransform(projection));
}

View File

@@ -171,7 +171,7 @@ export function svgVertices(projection, context) {
function drawTargets(selection, graph, entities, filter) {
var debugClass = 'pink';
var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
var targets = selection.selectAll('.vertex.target')
.filter(filter)
.data(entities, function key(d) { return d.id; });
@@ -184,10 +184,9 @@ export function svgVertices(projection, context) {
targets.enter()
.append('circle')
.attr('r', radiuses.shadow[3]) // just use the biggest one for now
.attr('class', function(d) { return 'node vertex target ' + d.id; })
.merge(targets)
.attr('transform', svgPointTransform(projection))
.classed(debugClass, context.getDebug('target'));
.attr('class', function(d) { return 'node vertex target ' + fillClass + d.id; })
.attr('transform', svgPointTransform(projection));
}