想象一下我们有这样的场景。一个3000x3000的大舞台,里面有许多节点。
用户想查看所有节点,但它们并不是一次性可见的。
如何展示和滚动一个非常大的 HTML5 画布?
让我们想象你有一个很大的画布,你想要增加导航功能。
我将向你展示4种不同的方法来实现这一点:
1. 只制作大舞台
这是最简单的方法。但它非常慢,因为大的画布运行速度慢。
用户将能够使用本机滚动条进行滚动。
优点:
- 实现简单
缺点:
- 速度慢
显示源代码!
<html>
<head>
<script src="https://unpkg.com/konva@9.3.18/konva.min.js"></script>
<meta charset="utf-8" />
<title>Konva Canvas Scrolling Demo</title>
<style>
body {
margin: 0;
padding: 0;
background-color: #f0f0f0;
height: 100%;
overflow: auto;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
var WIDTH = 3000;
var HEIGHT = 3000;
var NUMBER = 200;
var stage = new Konva.Stage({
container: 'container',
width: WIDTH,
height: HEIGHT,
});
var layer = new Konva.Layer();
stage.add(layer);
function generateNode() {
return new Konva.Circle({
x: WIDTH * Math.random(),
y: HEIGHT * Math.random(),
radius: 50,
fill: 'red',
stroke: 'black',
});
}
for (var i = 0; i < NUMBER; i++) {
layer.add(generateNode());
}
</script>
</body>
</html>
2. 使舞台可拖动(通过拖放导航)
这个方法更好,因为舞台要小得多。
优点:
- 实现简单
- 快速
缺点:
- 有时拖放导航并不是最佳用户体验
显示源代码!
<html>
<head>
<script src="https://unpkg.com/konva@9.3.18/konva.min.js"></script>
<meta charset="utf-8" />
<title>Konva Canvas Scrolling Drag Demo</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #f0f0f0;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
draggable: true,
});
var layer = new Konva.Layer();
stage.add(layer);
var WIDTH = 3000;
var HEIGHT = 3000;
var NUMBER = 200;
function generateNode() {
return new Konva.Circle({
x: WIDTH * Math.random(),
y: HEIGHT * Math.random(),
radius: 50,
fill: 'red',
stroke: 'black',
});
}
for (var i = 0; i < NUMBER; i++) {
layer.add(generateNode());
}
</script>
</body>
</html>
3. 仿真滚动条。
你将需要手动绘制它们并实现所有移动功能。
这工作量相当大,但对于许多应用而言有效。
说明:尝试使用滚动条进行滚动。
优点:
- 运行良好
- 直观的滚动
- 快速
缺点:
- 滚动条不是原生的,所以你必须手动实现许多功能(例如使用键盘滚动)
显示源代码!
<html>
<head>
<script src="https://unpkg.com/konva@9.3.18/konva.min.js"></script>
<meta charset="utf-8" />
<title>Konva Canvas Scrolling Drag Demo</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #f0f0f0;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
var layer = new Konva.Layer();
stage.add(layer);
var WIDTH = 3000;
var HEIGHT = 3000;
var NUMBER = 200;
function generateNode() {
return new Konva.Circle({
x: WIDTH * Math.random(),
y: HEIGHT * Math.random(),
radius: 50,
fill: 'red',
stroke: 'black',
});
}
for (var i = 0; i < NUMBER; i++) {
layer.add(generateNode());
}
// now draw our bars
var scrollLayers = new Konva.Layer();
stage.add(scrollLayers);
const PADDING = 5;
var verticalBar = new Konva.Rect({
width: 10,
height: 100,
fill: 'grey',
opacity: 0.8,
x: stage.width() - PADDING - 10,
y: PADDING,
draggable: true,
dragBoundFunc: function (pos) {
pos.x = stage.width() - PADDING - 10;
pos.y = Math.max(
Math.min(pos.y, stage.height() - this.height() - PADDING),
PADDING
);
return pos;
},
});
scrollLayers.add(verticalBar);
verticalBar.on('dragmove', function () {
// delta in %
const availableHeight =
stage.height() - PADDING * 2 - verticalBar.height();
var delta = (verticalBar.y() - PADDING) / availableHeight;
layer.y(-(HEIGHT - stage.height()) * delta);
});
var horizontalBar = new Konva.Rect({
width: 100,
height: 10,
fill: 'grey',
opacity: 0.8,
x: PADDING,
y: stage.height() - PADDING - 10,
draggable: true,
dragBoundFunc: function (pos) {
pos.x = Math.max(
Math.min(pos.x, stage.width() - this.width() - PADDING),
PADDING
);
pos.y = stage.height() - PADDING - 10;
return pos;
},
});
scrollLayers.add(horizontalBar);
horizontalBar.on('dragmove', function () {
// delta in %
const availableWidth =
stage.width() - PADDING * 2 - horizontalBar.width();
var delta = (horizontalBar.x() - PADDING) / availableWidth;
layer.x(-(WIDTH - stage.width()) * delta);
});
stage.on('wheel', function (e) {
// prevent parent scrolling
e.evt.preventDefault();
const dx = e.evt.deltaX;
const dy = e.evt.deltaY;
const minX = -(WIDTH - stage.width());
const maxX = 0;
const x = Math.max(minX, Math.min(layer.x() - dx, maxX));
const minY = -(HEIGHT - stage.height());
const maxY = 0;
const y = Math.max(minY, Math.min(layer.y() - dy, maxY));
layer.position({ x, y });
const availableHeight =
stage.height() - PADDING * 2 - verticalBar.height();
const vy =
(layer.y() / (-HEIGHT + stage.height())) * availableHeight + PADDING;
verticalBar.y(vy);
const availableWidth =
stage.width() - PADDING * 2 - horizontalBar.width();
const hx =
(layer.x() / (-WIDTH + stage.width())) * availableWidth + PADDING;
horizontalBar.x(hx);
});
</script>
</body>
</html>
4. 使用变换仿真屏幕移动。
这个演示效果很好,但可能有点棘手。
其思路是:
- 我们将使用与屏幕大小相等的小画布
- 我们将创建一个所需大小的容器(3000x3000),使本机滚动条可见
- 当用户尝试滚动时,我们将对舞台容器应用 CSS 变换,使其仍在用户屏幕中心
- 我们将移动所有节点,这样看起来就像你在滚动(通过更改舞台位置)
优点:
- 完美运行且快速
- 原生滚动
缺点:
- 你必须理解发生了什么。
说明:尝试使用本机滚动条进行滚动。
显示源代码!
<html>
<head>
<script src="https://unpkg.com/konva@9.3.18/konva.min.js"></script>
<meta charset="utf-8" />
<title>Konva Canvas Scrolling Demo</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #f0f0f0;
height: 100%;
overflow: auto;
}
#large-container {
width: 3000px;
height: 3000px;
overflow: hidden;
}
#scroll-container {
width: calc(100% - 22px);
height: calc(100vh - 22px);
overflow: auto;
margin: 10px;
border: 1px solid grey;
}
</style>
</head>
<body>
<div id="scroll-container">
<div id="large-container">
<div id="container"></div>
</div>
</div>
<script>
var WIDTH = 3000;
var HEIGHT = 3000;
var NUMBER = 200;
// padding will increase the size of stage
// so scrolling will look smoother
var PADDING = 500;
var stage = new Konva.Stage({
container: 'container',
width: window.innerWidth + PADDING * 2,
height: window.innerHeight + PADDING * 2,
});
var layer = new Konva.Layer();
stage.add(layer);
function generateNode() {
return new Konva.Circle({
x: WIDTH * Math.random(),
y: HEIGHT * Math.random(),
radius: 50,
fill: 'red',
stroke: 'black',
});
}
for (var i = 0; i < NUMBER; i++) {
layer.add(generateNode());
}
var scrollContainer = document.getElementById('scroll-container');
function repositionStage() {
var dx = scrollContainer.scrollLeft - PADDING;
var dy = scrollContainer.scrollTop - PADDING;
stage.container().style.transform =
'translate(' + dx + 'px, ' + dy + 'px)';
stage.x(-dx);
stage.y(-dy);
}
scrollContainer.addEventListener('scroll', repositionStage);
repositionStage();
</script>
</body>
</html>