Javascript 放大时如何显示全文&;缩小时将其截断

Javascript 放大时如何显示全文&;缩小时将其截断,javascript,css,d3.js,Javascript,Css,D3.js,我正在用d3.js创建一个树形图,效果很好。。。但我想让文本对缩放做出反应,下面是 请看第一个节点。。。它有很多字符(在我的例子中,最大值为255) 放大或缩小时,我的文本保持不变,但我希望在放大时看到所有内容 var json={ “名称”:“夏绿蒂丽西亚费尔南德斯夏绿蒂丽西亚费尔南德斯夏绿蒂丽西亚费尔南德斯夏绿蒂丽西亚费尔南德斯夏绿蒂丽西亚费尔南德斯夏绿蒂丽西亚费尔南德斯夏绿蒂丽西亚费尔南德斯夏绿蒂丽西亚费尔南德斯Maude asd”, “id”:“06ada7cd-3078-54bc-b

我正在用d3.js创建一个树形图,效果很好。。。但我想让文本对缩放做出反应,下面是

请看第一个节点。。。它有很多字符(在我的例子中,最大值为255)

放大或缩小时,我的文本保持不变,但我希望在放大时看到所有内容

var json={
“名称”:“夏绿蒂丽西亚费尔南德斯夏绿蒂丽西亚费尔南德斯夏绿蒂丽西亚费尔南德斯夏绿蒂丽西亚费尔南德斯夏绿蒂丽西亚费尔南德斯夏绿蒂丽西亚费尔南德斯夏绿蒂丽西亚费尔南德斯夏绿蒂丽西亚费尔南德斯Maude asd”,
“id”:“06ada7cd-3078-54bc-bb87-72e9d6f38abf”,
“_家长”:[{
“姓名”:“珍妮·克莱顿·诺顿”,
“id”:“a39bfa73-6617-5e8e-9470-d26b68787e52”,
“_家长”:[{
“名称”:“珍珠炮”,
“id”:“fc956046-a5c3-502f-b853-D66980428F”,
“_家长”:[{
“姓名”:“奥古斯塔·米勒”,
“id”:“fa5b0c07-9000-5475-a90e-b76af7693a57”
}, {
“姓名”:“克莱顿·韦尔奇”,
“id”:“3194517d-1151-502e-a3b6-d1ae8234c647”
}]
}, {
“姓名”:“内尔·莫顿”,
“id”:“06c7b0cb-cd21-53be-81bd-9b088af96904”,
“_家长”:[{
“姓名”:“Lelia Alexa Hernandez”,
“id”:“667d2bb6-c26e-5881-9bdc-7ac9805f96c2”
}, {
“姓名”:“兰迪·韦尔奇”,
“id”:“104039bb-d353-54a9-a4f2-09fda08b58bb”
}]
}]
}, {
“姓名”:“海伦·唐纳德·阿尔瓦拉多”,
“id”:“522266d2-f01a-5ec0-9977-622e4cb054c0”,
“_家长”:[{
“姓名”:“格西·格洛弗”,
“id”:“da430aa2-f438-51ed-ae47-2d9f76f8d831”,
“_家长”:[{
“姓名”:“米娜·弗里曼”,
“id”:“d384197e-2e1e-5fb2-987b-d90a5cdc3c15”
}, {
“姓名”:“夏洛特·阿赫兰德罗·马丁”,
“id”:“ea01728f-e542-53a6-acd0-6f43805c31a3”
}]
}, {
“名字”:“耶稣基督皮尔斯”,
“id”:“bfd1612c-b90d-5975-824c-49ecf62b3d5f”,
“_家长”:[{
“姓名”:“唐纳德·弗里曼·考克斯”,
“id”:“4f910be4-b827-50be-b783-6ba3249f6ebc”
}, {
“姓名”:“亚历克斯·费尔南德斯·冈萨雷斯”,
“id”:“efb2396d-478a-5cbc-b168-52e028452f3b”
}]
}]
}]
};
var boxWidth=250,
箱高=100;
//设置缩放和平移
var zoom=d3.behavior.zoom()
.scaleExtent([1,1])
.on('zoom',function(){
attr(“transform”、“translate”(+d3.event.translate+))比例(+d3.event.scale+);
})
//偏移,以便第一次平移和缩放不会跳回原点
.翻译([600600]);
var svg=d3.选择(“正文”).追加(“svg”)
.attr('宽度',1000)
.attr('height',500)
.呼叫(缩放)
.append('g')
//树的左侧填充,以便整个根节点都显示在屏幕上。
//TODO:找到更好的方法
.attr(“转换”、“翻译(150200)”);
var tree=d3.layout.tree()
//使用nodeSize,我们可以控制
//节点之间的分离。如果我们使用
//而d3将使用size参数
//动态计算要填充的间距
//可用空间。
.nodeSize([100200])
//默认情况下,表兄弟姐妹之间的距离比兄弟姐妹远。
//通过在所有情况下返回相同的值,我们可以绘制表兄妹
//与兄弟姐妹之间的距离相同。
.分离(职能({
回报9;
})
//告诉d3子节点是什么。记住,我们在画画
//一棵树,使祖先成为子节点。
.儿童(功能(人){
返回人。\u父母;
});
var nodes=tree.nodes(json),
链接=树。链接(节点);
//样式链接(边)
svg.selectAll(“path.link”)
.数据(链接)
.enter().append(“路径”)
.attr(“类”、“链接”)
.attr(“d”,肘部);
//样式节点
var node=svg.selectAll(“g.person”)
.数据(节点)
.enter().append(“g”)
.attr(“类”、“人”)
.attr(“转换”,函数(d){
返回“translate”(“+d.y+”,“+d.x+”);
});
//绘制矩形人物框
node.append(“rect”)
艾特先生({
x:-(箱宽/2),
y:-(箱高/2),
宽度:boxWidth,
高度:箱高
});
//画出这个人的名字并把它放在盒子里
node.append(“文本”)
.attr(“文本锚定”、“开始”)
.attr('class','name')
.文本(功能(d){
返回d.name;
});
//使用d3plus在所有节点上进行文本换行。默认情况下,没有任何左或右键
//右填充。要添加填充,我们需要绘制另一个矩形,
//在带边框的矩形内,表示我们要显示的区域
//喜欢要包含在中的文本。
d3.选择全部(“文本”)。每个功能(d、i){
d3plus.textwrap()
.容器(d3.选择(此))
.valign(“中间”)
.draw();
});
/**
*创建直线连接线的自定义路径函数。
*/
功能弯头(d){
返回“M”+d.source.y+”,“+d.source.x+“H”+(d.source.y+(d.target.y-d.source.y)/2)+“V”+d.target.x+“H”+d.target.y;
}
正文{
文本对齐:居中;
}
svg{
边缘顶部:32px;
边框:1px实心#aaa;
}
.person rect{
填充:#fff;
笔画:钢蓝;
笔画宽度:1px;
}
.人{
字体:14px无衬线;
}
.链接{
填充:无;
冲程:#ccc;
笔划宽度:1.5px;
}

我在这篇文章中对您的要求做了一个示例

它可能需要更多的调整来定位文本的垂直中间位置;但这可能是你工作的基础。计算在函数
wrap()
中完成,并调用页面加载和缩放

function wrap() {
  var texts = d3.selectAll("text"),
    lineHeight = 1.1, // ems
    padding = 2, // px
    fSize = scale > 1 ? fontSize / scale : fontSize,
    // find how many lines can be included
    lines = Math.floor((boxHeight - (2 * padding)) / (lineHeight * fSize)) || 1;
  texts.each(function(d, i) {
    var text = d3.select(this),
      words = d.name.split(/\s+/).reverse(),
      word,
      line = [],
      lineNumber = 0,
      tspan = text.text(null).append("tspan").attr("dy", "-0.5em").style("font-size", fSize + "px");
    while ((word = words.pop())) {
      line.push(word);
      tspan.text(line.join(" "));
      // check if the added word can fit in the box
      if ((tspan.node().getComputedTextLength() + (2 * padding)) > boxWidth) {
        // remove current word from line
        line.pop();
        tspan.text(line.join(" "));
        lineNumber++;
        // check if a new line can be placed
        if (lineNumber > lines) {
          // left align text of last line
          tspan.attr("x", (tspan.node().getComputedTextLength() - boxWidth) / 2 + padding);
          --lineNumber;
          break;
        }
        // create new line
        tspan.text(line.join(" "));
        line = [word]; // place the current word in new line
        tspan = text.append("tspan")
          .style("font-size", fSize + "px")
          .attr("dy", "1em")
          .text(word);
      }
      // left align text
      tspan.attr("x", (tspan.node().getComputedTextLength() - boxWidth) / 2 + padding);
    }
    // align vertically inside the box
    text.attr("text-anchor", "middle").attr("y", padding - (lineHeight * fSize * lineNumber) / 2);
  });
}

还请注意,我添加了样式
主导基线:悬挂
。person

文本包装可能是过程密集型的。为了解决中存在的这些问题,由于预渲染,这提高了性能

此脚本在
DOM
之外创建一个元素,并将所有节点和边存储到其中。然后检查哪些元素是可见的,将它们从
DOM
中删除,并在适当的时候添加回来

我正在使用
jQuery
获取
data()
,并选择ele
$(function() {

    var viewport_width = $(window).width(),
        viewport_height = $(window).height(),
        node_width = 120,
        node_height = 60,
        separation_width = 100,
        separation_height = 55,
        node_separation = 0.78,
        font_size = 20,
        refresh_delay = 200,
        refresh_timeout,

        zoom_extent = [0.5, 1.15],

        // Element outside DOM, to calculate pre-render
        buffer = $("<div>");

    // Parse "transform" attribute
    function parse_transform(input_string) {
        var transformations = {},
            matches, seek;
        for (matches in input_string = input_string.match(/(\w+)\(([^,)]+),?([^)]+)?\)/gi)) {
            seek = input_string[matches].match(/[\w.\-]+/g), transformations[seek.shift()] = seek;
        }
        return transformations;
    }

    // Adapted from ConnorsFan's answer
    function get_font_size(scale) {
        fs = ~~Math.min(font_size, 15 * Math.pow(scale, -0.25));
        fs = ~~(((font_size / scale) + fs) / 2)
        return [fs, fs]
    }

    // Use d3plus to wrap the text
    function wrap_text(scale) {
        if (scale > 0.75) {
            $("svg > g > g").each(function(a, b) {
                f = $(b);
                $("text", f)
                    .text(f.data("text"));
            });
            d3.selectAll("text").each(function(a, b) {
                d3_el = d3.select(this);

                d3plus.textwrap()
                    .container(d3_el)
                    .align("center")
                    .valign("middle")
                    .width(node_width)
                    .height(node_height)
                    .valign("middle")
                    .resize(!0)
                    .size(get_font_size(scale))
                    .draw();
            });
        }
    }

    // Handle pre-render (remove elements that leave viewport, add them back when appropriate) 
    function pre_render() {
        buffer.children("*")
            .each(function(i, el) {
                d3.transform(d3.select(el).attr("transform"));
                var el_path = $(el)[0],
                    svg_wrapper = $("svg"),
                    t = parse_transform($("svg > g")[0].getAttribute("transform")),

                    element_data = $(el_path).data("coords"),

                    element_min_x = ~~element_data.min_x,
                    element_max_x = ~~element_data.max_x,
                    element_min_y = ~~element_data.min_y,
                    element_max_y = ~~element_data.max_y,

                    svg_wrapper_width = svg_wrapper.width(),
                    svg_wrapper_height = svg_wrapper.height(),

                    s = parseFloat(t.scale),
                    x = ~~t.translate[0],
                    y = ~~t.translate[1];

                if (element_min_x * s + x <= svg_wrapper_width &&
                    element_min_y * s + y <= svg_wrapper_height &&
                    0 <= element_max_x * s + x &&
                    0 <= element_max_y * s + y) {

                    if (0 == $("#" + $(el).prop("id")).length) {

                        if (("n" == $(el).prop("id").charAt(0))) {
                            // insert nodes above edges
                            $(el).clone(1).appendTo($("svg > g"));
                            wrap_text(scale = t.scale);
                        } else {
                            // insert edges
                            $(el).clone(1).prependTo($("svg > g"));
                        }
                    }
                } else {

                    id = $(el).prop("id");
                    $("#" + id).remove();
                }
            });
    }
    d3.scale.category20();
    var link = d3.select("body")
        .append("svg")
        .attr("width", viewport_width)
        .attr("height", viewport_height)
        .attr("pointer-events", "all")
        .append("svg:g")
        .call(d3.behavior.zoom().scaleExtent(zoom_extent)),
        layout_tree = d3.layout.tree()
        .nodeSize([separation_height * 2, separation_width * 2])
        .separation(function() {
            return node_separation;
        })
        .children(function(a) {
            return a._parents;
        }),
        nodes = layout_tree.nodes(json),
        edges = layout_tree.links(nodes);

    // Style links (edges)
    link.selectAll("path.link")
        .data(edges)
        .enter()
        .append("path")
        .attr("class", "link")
        .attr("d", function(a) {
            return "M" + a.source.y + "," + a.source.x + "H" + ~~(a.source.y + (a.target.y - a.source.y) / 2) + "V" + a.target.x + "H" + a.target.y;
        });

    // Style nodes
    var node = link.selectAll("g.person")
        .data(nodes)
        .enter()
        .append("g")
        .attr("transform", function(a) {
            return "translate(" + a.y + "," + a.x + ")";
        })
        .attr("class", "person");

    // Draw the rectangle person boxes
    node.append("rect")
        .attr({
            x: -(node_width / 2),
            y: -(node_height / 2),
            width: node_width,
            height: node_height
        });

    // Draw the person's name and position it inside the box
    node_text = node.append("text")
        .attr("text-anchor", "start")
        .text(function(a) {
            return a.name;
        });

    // Text wrap on all nodes using d3plus. By default there is not any left or
    // right padding. To add padding we would need to draw another rectangle,
    // inside of the rectangle with the border, that represents the area we would
    // like the text to be contained in.
    d3.selectAll("text")
        .each(function(a, b) {
            d3plus.textwrap()
                .container(d3.select(this))
                .valign("middle")
                .resize(!0)
                .size(get_font_size(1))
                .draw();
        });

    // START Create off-screen render

    // Append node edges to memory, to allow pre-rendering
    $("svg > g > path")
        .each(function(a, b) {
            el = $(b)[0];
            if (d = $(el)
                .attr("d")) {
                // Parse d parameter from rect, in the format found in the d3 tree dom: M0,0H0V0V0
                for (var g = d.match(/([MLQTCSAZVH])([^MLQTCSAZVH]*)/gi), c = g.length, h, k, f, l, m = [], e = [], n = 0; n < c; n++) {
                    command = g[n], void 0 !== command && ("M" == command.charAt(0) ? (coords = command.substring(1, command.length), m.push(~~coords.split(",")[0]), e.push(~~coords.split(",")[1])) : "V" == command.charAt(0) ? e.push(~~command.substring(1, command.length)) : "H" == command.charAt(0) && m.push(~~command.substring(1, command.length)));
                }
                0 < m.length && (h = Math.min.apply(this, m), f = Math.max.apply(this, m));
                0 < e.length && (k = Math.min.apply(this, e), l = Math.max.apply(this, e));
                $(el).data("position", a);
                $(el).prop("id", "e" + a);
                $(el).data("coords", {
                    min_x: h,
                    min_y: k,
                    max_x: f,
                    max_y: l
                });
                // Store element coords in memory
                hidden_element = $(el).clone(1);
                buffer.append(hidden_element);
            }
        });

    // Append node elements to memory
    $("svg > g > g").each(function(a, b) {
        el = $("rect", b);
        transform = b.getAttribute("transform");
        null !== transform && void 0 !== transform ? (t = parse_transform(transform), tx = ~~t.translate[0], ty = ~~t.translate[1]) : ty = tx = 0;
        // Calculate element area
        el_min_x = ~~el.attr("x");
        el_min_y = ~~el.attr("y");
        el_max_x = ~~el.attr("x") + ~~el.attr("width");
        el_max_y = ~~el.attr("y") + ~~el.attr("height");
        $(b).data("position", a);
        $(b).prop("id", "n" + a);
        $(b).data("coords", {
            min_x: el_min_x + tx,
            min_y: el_min_y + ty,
            max_x: el_max_x + tx,
            max_y: el_max_y + ty
        });
        text_el = $("text", $(b));
        0 < text_el.length && $(b).data("text", d3.select(text_el[0])[0][0].__data__.name);

        // Store element coords in memory
        hidden_element = $(b).clone(1);
        // store node in memory
        buffer.append(hidden_element);
    });

    // END Create off-screen render

    d3_svg = d3.select("svg");
    svg_group = d3.select("svg > g");

    // Setup zoom and pan
    zoom = d3.behavior.zoom()
        .on("zoom", function() {
            previous_transform = $("svg > g")[0].getAttribute("transform");
            svg_group.style("stroke-width", 1.5 / d3.event.scale + "px");
            svg_group.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
            pre_render();

            if (previous_transform !== null) {
                previous_transform = parse_transform(previous_transform);
                if (previous_transform.scale != d3.event.scale) {

                    // ConnorsFan's solution
                    if (refresh_timeout) {
                        clearTimeout(refresh_timeout);
                    }
                    scale = d3.event.scale;
                    refresh_timeout = setTimeout(function() {
                        wrap_text(scale = scale);
                    }, refresh_delay, scale);

                }
            }
        });
    // Apply initial zoom / pan
    d3_svg.call(zoom);
});
var scaleValue = 1;
var refreshTimeout;
var refreshDelay = 0;

var zoom = d3.behavior.zoom()
    .scaleExtent([.1, 1.5])
    .on('zoom', function () {
        svg.attr("transform", "translate(" + d3.event.translate + ") scale(" + d3.event.scale + ")");
        scaleValue = d3.event.scale;
        if (refreshTimeout) {
            clearTimeout(refreshTimeout);
        }
        refreshTimeout = setTimeout(function () {
            wrapText();
        }, refreshDelay);
    })
// Calculate the refresh delay
refreshDelay = Math.pow(node.size(), 0.5) * 2.0;
// Calculate the font size for the current scaling
var calcFontSize = function () {
    return Math.min(24, 10 * Math.pow(scaleValue, -0.25))
}
node.append("rect")
    .attr({
        x: 0,
        y: -(boxHeight / 2),
        width: boxWidth,
        height: boxHeight
    });

node.append("text")
    .attr("text-anchor", "start")
    .attr("dominant-baseline", "middle")
    .attr('class', 'name')
    .text(function (d) {
        return d.name;
    });
// Adjust the font size to the zoom level and wrap the text in the container
var wrapText = function () {
    d3.selectAll("text").each(function (d, i) {
        var $text = d3.select(this);
        if (!$text.attr("data-original-text")) {
            // Save original text in custom attribute
            $text.attr("data-original-text", $text.text());
        }
        var content = $text.attr("data-original-text");
        var tokens = content.split(/(\s)/g);
        var strCurrent = "";
        var strToken = "";
        var box;
        var lineHeight;
        var padding = 4;
        $text.text("").attr("font-size", calcFontSize());
        var $tspan = $text.append("tspan").attr("x", padding).attr("dy", 0);
        while (tokens.length > 0) {
            strToken = tokens.shift();
            $tspan.text((strCurrent + strToken).trim());
            box = $text.node().getBBox();
            if (!lineHeight) {
                lineHeight = box.height;
            }
            if (box.width > boxWidth - 2 * padding) {
                $tspan.text(strCurrent.trim());
                if (box.height + lineHeight < boxHeight) {
                    strCurrent = strToken;
                    $tspan = $text.append("tspan").attr("x", padding).attr("dy", lineHeight).text(strCurrent.trim());
                } else {
                    break;
                }
            }
            else {
                strCurrent += strToken;
            }
        }
        $text.attr("y", -(box.height - lineHeight) / 2);
    });
}