需要一种算法来调整组织结构图的大小以适应 window
Need an algorithm to resize an organization chart to fit window
上下文:
我需要能够将任何组织层次结构树呈现到任何给定尺寸的网页上,然后使用无头浏览器将页面转换为 PDF,同时保持文本清晰,以便大公司可以打印以大字体打印或放大查看。
最终目标:
- 公司可以通过应用创建其组织的层次结构
- 通过网页界面直观预览
- 保存为 PDF(并可选择打印物理副本)
这使他们能够拥有单一的事实来源并直观地表示其组织的树。一些组织希望定义较大的 pdf 尺寸(例如,24" x 36"),并能够将其闪存驱动器上的 PDF 带到 Kinkos 并打印出来并在他们的办公室展示。
注意:组织的深度或宽度没有上限。目前拥有超过 300 家公司和 5 个层次的公司。所以这就是为什么我需要一种可以扩展的方法。
当前方法:
目前,我正在通过缩放父容器的字体大小来采用蛮力方法。
- 将图表元素的字体大小设置为 0
- 创建一个 while 循环并将字体大小增加一些增量(例如 0.1px)
- 让浏览器重新绘制图表
- 检查是否溢出
- 如果没有溢出,递增并重复,否则,递减增量并退出循环
大图表需要更精细的增量,否则它们根本不会呈现。
例如,假设字体大小增量为 0.5。如果图表太大 0.5px 溢出,当前脚本将递增 - 看到它溢出 - 然后恢复为 0,这意味着它不显示。
如果我降低增量,那么大多数较小的图表渲染时间将急剧增加。
数据结构:
Seat: {
name: 'Seat Name',
children: Array<Seat>,
}
原型:
https://github.com/danfoust/org-chart-prototype
期望的解决方案
我可以将递归数据结构和 window 维度提供给函数,它可以有效地确定最佳字体大小以适应 window 中生成的 DOM。
如果 CSS 可以做到这一点,那就太好了。我正在使用 Puppeteer 并扩展为 Chromium,所以如果有一些最新和最伟大的 CSS 魔法,那就太棒了。
我按照@user3386109 的建议找到了上限,然后进行了二分查找。对于 300 个席位,这将查找时间缩短了一半。
'use strict';
const $ = (selector) => document.querySelector(selector);
const $$ = (selector) => document.querySelectorAll(selector);
// Prevent loop from blocking render for 3 ms
async function isOutOfBounds(chart, w, h) {
return new Promise((res) => {
setTimeout(() => res(chart.scrollWidth > w || chart.scrollHeight > h), 3);
});
}
///////////////////////////////////////////////////////////////////////////////////
const scrollbarPX = 16; // Prevent empty last page
const screenWidth = window.innerWidth - 1;
const screenHeight = window.innerHeight - scrollbarPX;
const headHeight = $('.header').scrollHeight;
const footHeight = $('.footer').scrollHeight;
const maxChartHeight = screenHeight - headHeight - footHeight;
(async () => {
// Set each page to window dimensions
const pages = $$('.page');
for (const page of pages) {
page.style.width = `${screenWidth}px`;
page.style.height = `${screenHeight}px`;
}
//////////////////////////////////////////////////////////////////////
const cache = {};
const charts = $$('.chart');
for (const chart of charts) {
let cacheKey = `${chart.scrollWidth}x${chart.scrollHeight}`;
if (cache[cacheKey]) {
chart.style.fontSize = `${cache[cacheKey]}px`;
// Handle rare case where same sized charts render differently at same font-size
if (!await isOutOfBounds(chart, screenWidth, maxChartHeight)) {
continue;
}
}
//////////////////////////////////////////////////////////////////
let max = 1;
while (1) { // Get upper bound
chart.style.fontSize = `${max}px`;
if (await isOutOfBounds(chart, screenWidth, maxChartHeight)) break;
max *= 2;
}
let mid;
while (1) { // Use binary search
mid = max / 2;
chart.style.fontSize = `${mid}px`;
if (!(await isOutOfBounds(chart, screenWidth, maxChartHeight))) break;
max = mid;
}
// The binary search takes the first number that works
// which may not be the best number. Let's try scaling
// up by a fraction of the working number.
let incr = mid / 10;
let size = mid + incr;
while (1) {
chart.style.fontSize = `${size}px`;
if (await isOutOfBounds(chart, screenWidth, maxChartHeight)) {
chart.style.fontSize = `${size - incr}px`;
cache[cacheKey] = size - incr;
break;
}
size += incr;
}
}
// Using waitForSelector with Puppeteer
$('body').classList.add('complete');
})();
上下文:
我需要能够将任何组织层次结构树呈现到任何给定尺寸的网页上,然后使用无头浏览器将页面转换为 PDF,同时保持文本清晰,以便大公司可以打印以大字体打印或放大查看。
最终目标:
- 公司可以通过应用创建其组织的层次结构
- 通过网页界面直观预览
- 保存为 PDF(并可选择打印物理副本)
这使他们能够拥有单一的事实来源并直观地表示其组织的树。一些组织希望定义较大的 pdf 尺寸(例如,24" x 36"),并能够将其闪存驱动器上的 PDF 带到 Kinkos 并打印出来并在他们的办公室展示。
注意:组织的深度或宽度没有上限。目前拥有超过 300 家公司和 5 个层次的公司。所以这就是为什么我需要一种可以扩展的方法。
当前方法:
目前,我正在通过缩放父容器的字体大小来采用蛮力方法。
- 将图表元素的字体大小设置为 0
- 创建一个 while 循环并将字体大小增加一些增量(例如 0.1px)
- 让浏览器重新绘制图表
- 检查是否溢出
- 如果没有溢出,递增并重复,否则,递减增量并退出循环
大图表需要更精细的增量,否则它们根本不会呈现。
例如,假设字体大小增量为 0.5。如果图表太大 0.5px 溢出,当前脚本将递增 - 看到它溢出 - 然后恢复为 0,这意味着它不显示。
如果我降低增量,那么大多数较小的图表渲染时间将急剧增加。
数据结构:
Seat: {
name: 'Seat Name',
children: Array<Seat>,
}
原型:
https://github.com/danfoust/org-chart-prototype
期望的解决方案
我可以将递归数据结构和 window 维度提供给函数,它可以有效地确定最佳字体大小以适应 window 中生成的 DOM。
如果 CSS 可以做到这一点,那就太好了。我正在使用 Puppeteer 并扩展为 Chromium,所以如果有一些最新和最伟大的 CSS 魔法,那就太棒了。
我按照@user3386109 的建议找到了上限,然后进行了二分查找。对于 300 个席位,这将查找时间缩短了一半。
'use strict';
const $ = (selector) => document.querySelector(selector);
const $$ = (selector) => document.querySelectorAll(selector);
// Prevent loop from blocking render for 3 ms
async function isOutOfBounds(chart, w, h) {
return new Promise((res) => {
setTimeout(() => res(chart.scrollWidth > w || chart.scrollHeight > h), 3);
});
}
///////////////////////////////////////////////////////////////////////////////////
const scrollbarPX = 16; // Prevent empty last page
const screenWidth = window.innerWidth - 1;
const screenHeight = window.innerHeight - scrollbarPX;
const headHeight = $('.header').scrollHeight;
const footHeight = $('.footer').scrollHeight;
const maxChartHeight = screenHeight - headHeight - footHeight;
(async () => {
// Set each page to window dimensions
const pages = $$('.page');
for (const page of pages) {
page.style.width = `${screenWidth}px`;
page.style.height = `${screenHeight}px`;
}
//////////////////////////////////////////////////////////////////////
const cache = {};
const charts = $$('.chart');
for (const chart of charts) {
let cacheKey = `${chart.scrollWidth}x${chart.scrollHeight}`;
if (cache[cacheKey]) {
chart.style.fontSize = `${cache[cacheKey]}px`;
// Handle rare case where same sized charts render differently at same font-size
if (!await isOutOfBounds(chart, screenWidth, maxChartHeight)) {
continue;
}
}
//////////////////////////////////////////////////////////////////
let max = 1;
while (1) { // Get upper bound
chart.style.fontSize = `${max}px`;
if (await isOutOfBounds(chart, screenWidth, maxChartHeight)) break;
max *= 2;
}
let mid;
while (1) { // Use binary search
mid = max / 2;
chart.style.fontSize = `${mid}px`;
if (!(await isOutOfBounds(chart, screenWidth, maxChartHeight))) break;
max = mid;
}
// The binary search takes the first number that works
// which may not be the best number. Let's try scaling
// up by a fraction of the working number.
let incr = mid / 10;
let size = mid + incr;
while (1) {
chart.style.fontSize = `${size}px`;
if (await isOutOfBounds(chart, screenWidth, maxChartHeight)) {
chart.style.fontSize = `${size - incr}px`;
cache[cacheKey] = size - incr;
break;
}
size += incr;
}
}
// Using waitForSelector with Puppeteer
$('body').classList.add('complete');
})();