Skip to content

Commit 84427d5

Browse files
authored
Merge pull request #1409 from plotly/rangeslider-crisp-round
Better looking range slider handles
2 parents 122d3d5 + 696519b commit 84427d5

12 files changed

+36
-28
lines changed

src/components/rangeslider/constants.js

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,10 @@ module.exports = {
4141
grabAreaFill: 'transparent',
4242
grabAreaCursor: 'col-resize',
4343
grabAreaWidth: 10,
44-
grabAreaMinOffset: -6,
45-
grabAreaMaxOffset: -2,
4644

47-
handleWidth: 2,
45+
handleWidth: 4,
4846
handleRadius: 1,
49-
handleFill: '#fff',
50-
handleStroke: '#666',
47+
handleStrokeWidth: 1,
5148

5249
extraPad: 15
5350
};

src/components/rangeslider/draw.js

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,9 @@ module.exports = function(gd) {
9595
opts._height = (fullLayout.height - margin.b - margin.t) * opts.thickness;
9696
opts._offsetShift = Math.floor(opts.borderwidth / 2);
9797

98-
var x = margin.l + (graphSize.w * domain[0]);
98+
var x = Math.round(margin.l + (graphSize.w * domain[0]));
9999

100-
var y = (
100+
var y = Math.round(
101101
margin.t + graphSize.h * (1 - oppDomain[0]) +
102102
tickHeight +
103103
opts._offsetShift + constants.extraPad
@@ -252,11 +252,16 @@ function setDataRange(rangeSlider, gd, axisOpts, opts) {
252252
}
253253

254254
function setPixelRange(rangeSlider, gd, axisOpts, opts) {
255+
var hw2 = constants.handleWidth / 2;
255256

256257
function clamp(v) {
257258
return Lib.constrain(v, 0, opts._width);
258259
}
259260

261+
function clampHandle(v) {
262+
return Lib.constrain(v, -hw2, opts._width + hw2);
263+
}
264+
260265
var pixelMin = clamp(opts.d2p(axisOpts._rl[0])),
261266
pixelMax = clamp(opts.d2p(axisOpts._rl[1]));
262267

@@ -271,11 +276,17 @@ function setPixelRange(rangeSlider, gd, axisOpts, opts) {
271276
.attr('x', pixelMax)
272277
.attr('width', opts._width - pixelMax);
273278

279+
// ..
280+
var offset = 0.5;
281+
282+
var xMin = Math.round(clampHandle(pixelMin - hw2)) - offset,
283+
xMax = Math.round(clampHandle(pixelMax - hw2)) + offset;
284+
274285
rangeSlider.select('g.' + constants.grabberMinClassName)
275-
.attr('transform', 'translate(' + (pixelMin - constants.handleWidth - 1) + ',0)');
286+
.attr('transform', 'translate(' + xMin + ',' + offset + ')');
276287

277288
rangeSlider.select('g.' + constants.grabberMaxClassName)
278-
.attr('transform', 'translate(' + pixelMax + ',0)');
289+
.attr('transform', 'translate(' + xMax + ',' + offset + ')');
279290
}
280291

281292
function drawBg(rangeSlider, gd, axisOpts, opts) {
@@ -295,14 +306,15 @@ function drawBg(rangeSlider, gd, axisOpts, opts) {
295306
opts.borderwidth - 1;
296307

297308
var offsetShift = -opts._offsetShift;
309+
var lw = Drawing.crispRound(gd, opts.borderwidth);
298310

299311
bg.attr({
300312
width: opts._width + borderCorrect,
301313
height: opts._height + borderCorrect,
302314
transform: 'translate(' + offsetShift + ',' + offsetShift + ')',
303315
fill: opts.bgcolor,
304316
stroke: opts.bordercolor,
305-
'stroke-width': opts.borderwidth,
317+
'stroke-width': lw
306318
});
307319
}
308320

@@ -415,7 +427,8 @@ function drawMasks(rangeSlider, gd, axisOpts, opts) {
415427

416428
maskMin.enter().append('rect')
417429
.classed(constants.maskMinClassName, true)
418-
.attr({ x: 0, y: 0 });
430+
.attr({ x: 0, y: 0 })
431+
.attr('shape-rendering', 'crispEdges');
419432

420433
maskMin
421434
.attr('height', opts._height)
@@ -426,7 +439,8 @@ function drawMasks(rangeSlider, gd, axisOpts, opts) {
426439

427440
maskMax.enter().append('rect')
428441
.classed(constants.maskMaxClassName, true)
429-
.attr('y', 0);
442+
.attr('y', 0)
443+
.attr('shape-rendering', 'crispEdges');
430444

431445
maskMax
432446
.attr('height', opts._height)
@@ -442,7 +456,8 @@ function drawSlideBox(rangeSlider, gd, axisOpts, opts) {
442456
slideBox.enter().append('rect')
443457
.classed(constants.slideBoxClassName, true)
444458
.attr('y', 0)
445-
.attr('cursor', constants.slideBoxCursor);
459+
.attr('cursor', constants.slideBoxCursor)
460+
.attr('shape-rendering', 'crispEdges');
446461

447462
slideBox.attr({
448463
height: opts._height,
@@ -470,14 +485,15 @@ function drawGrabbers(rangeSlider, gd, axisOpts, opts) {
470485
x: 0,
471486
width: constants.handleWidth,
472487
rx: constants.handleRadius,
473-
fill: constants.handleFill,
474-
stroke: constants.handleStroke,
488+
fill: Color.background,
489+
stroke: Color.defaultLine,
490+
'stroke-width': constants.handleStrokeWidth,
475491
'shape-rendering': 'crispEdges'
476492
};
477493

478494
var handleDynamicAttrs = {
479-
y: opts._height / 4,
480-
height: opts._height / 2,
495+
y: Math.round(opts._height / 4),
496+
height: Math.round(opts._height / 2),
481497
};
482498

483499
var handleMin = grabberMin.selectAll('rect.' + constants.handleMinClassName)
@@ -500,6 +516,7 @@ function drawGrabbers(rangeSlider, gd, axisOpts, opts) {
500516

501517
var grabAreaFixAttrs = {
502518
width: constants.grabAreaWidth,
519+
x: 0,
503520
y: 0,
504521
fill: constants.grabAreaFill,
505522
cursor: constants.grabAreaCursor
@@ -510,20 +527,14 @@ function drawGrabbers(rangeSlider, gd, axisOpts, opts) {
510527
grabAreaMin.enter().append('rect')
511528
.classed(constants.grabAreaMinClassName, true)
512529
.attr(grabAreaFixAttrs);
513-
grabAreaMin.attr({
514-
x: constants.grabAreaMinOffset,
515-
height: opts._height
516-
});
530+
grabAreaMin.attr('height', opts._height);
517531

518532
var grabAreaMax = grabberMax.selectAll('rect.' + constants.grabAreaMaxClassName)
519533
.data([0]);
520534
grabAreaMax.enter().append('rect')
521535
.classed(constants.grabAreaMaxClassName, true)
522536
.attr(grabAreaFixAttrs);
523-
grabAreaMax.attr({
524-
x: constants.grabAreaMaxOffset,
525-
height: opts._height
526-
});
537+
grabAreaMax.attr('height', opts._height);
527538
}
528539

529540
function clearPushMargins(gd) {
Loading
Loading

test/image/baselines/ohlc_first.png

-128 Bytes
Loading

test/image/baselines/range_slider.png

-2 Bytes
Loading
13 Bytes
Loading
10 Bytes
Loading
Loading
-112 Bytes
Loading
201 Bytes
Loading

test/jasmine/tests/range_slider_test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ describe('the range slider', function() {
3939
var transformParts = node.getAttribute('transform').split('(');
4040

4141
expect(transformParts[0]).toEqual('translate');
42-
expect(+transformParts[1].split(',0)')[0]).toBeWithin(val, TOL);
42+
expect(+transformParts[1].split(',0.5)')[0]).toBeWithin(val, TOL);
4343
}
4444

4545
describe('when specified as visible', function() {
@@ -99,7 +99,7 @@ describe('the range slider', function() {
9999

100100
expect(gd.layout.xaxis.range).toBeCloseToArray([4, 49], -0.5);
101101
expect(maskMin.getAttribute('width')).toEqual(String(diff));
102-
expect(handleMin.getAttribute('transform')).toBe('translate(' + (diff - 3) + ',0)');
102+
expect(handleMin.getAttribute('transform')).toBe('translate(' + (diff - 2.5) + ',0.5)');
103103
}).then(done);
104104
});
105105

@@ -204,7 +204,7 @@ describe('the range slider', function() {
204204
expect(+maskMin.getAttribute('width')).toBeWithin(126, TOL);
205205
expect(+maskMax.getAttribute('width')).toEqual(0);
206206
testTranslate1D(handleMin, 123.32);
207-
testTranslate1D(handleMax, 619);
207+
testTranslate1D(handleMax, 617);
208208
})
209209
.then(done);
210210
});

0 commit comments

Comments
 (0)