【问题标题】:Javascript and SVG. Is there a way to detect has text rendered?Javascript 和 SVG。有没有办法检测文本是否呈现?
【发布时间】:2016-12-28 00:27:21
【问题描述】:

我得到了一些动态生成的svg 元素。它是根据input 字段中的文本生成的。

每个中断都意味着新的组元素结构类似

<g>
  <svg>
     <text/>
     <rect/>
  </svg>
</g>

还增加了字体更改选项。因此,在字体类型更改后,我需要在文本下重新绘制 rect,其大小取决于我在 text.getBBox() 数据上得到的内容。

问题是,当我第一次更改字体时(有事件触发,在您在select 下拉列表中选择其他字体类型后)我的矩形没有重新绘制,因为文本没有在 @ 内呈现987654333@ 元素,那么有没有办法检查所有&lt;text&gt; 元素中是否呈现了所有文本?

这里是问题的说明

UPD 1

这对我不起作用

someSvgTextBlocks.ready(function() { 
 //call redraw rects functions here
})

UPD 2

可悲的是,拉斐尔的回答也没有帮助我:

UPD 3

我使用 Angularjs 框架,所以这里是 Angular 的代码(我猜)

svg 的指令视图:

<svg ng-repeat="line in svgConfig.text track by $index">
    <g>
        <rect x="0" y="0" 
            ng-show="svgConfig.rectConfig[$index].active"
            ng-attr-height="{{svgConfig.rectConfig[$index].height}}" 
            ng-attr-width="{{svgConfig.rectConfig[$index].width}}" 
        >
        </rect>

        <text ng-attr-font-family="{{svgConfig.textConfig.fontFamily}}" 
            ng-attr-font-size="{{svgConfig.textConfig.fontSize}}"
            ng-attr-fill="{{svgConfig.textConfig.fontColor}}"
        >
            {{line}}
        </text>     

    </g>

</svg>

指令本身:

app.directive('imageTxtSvgDirective', ['imageTxtSvgService', 'svgUtilsService', function(imageTxtSvgService, svgUtilsService) {

    /**
    * Set event bindings
    */
    var setDomBindings = function($scope, $element, $attrs){
        /**
        * Sets watch to detect changes in text, fontsize, font family to recalculate binded svg-data
        */
        $scope.$watchGroup(['svgConfig.text', 'svgConfig.textConfig.fontSize', 'svgConfig.textConfig.fontFamily', 'svgConfig.extras', 'svgConfig.rectsVisible'], function() {
            var domText = $element.find('text'),
                textExampleList = domText,
                textConfig = $scope.svgConfig.textConfig,
                font = textConfig.fontFamily,
                size = textConfig.fontSize;

            document.fonts.load(''+size + 'px ' + font+'').then(
                function(){
                    $scope.setSvgRectanglesConfig(textExampleList);     
                }
            );
        });
    }

    /*
    * Retruns initialized DOM element
    */
    return {
        restrict: 'E',
        templateUrl: './app/shared/imageTextEditor/imageTxtSvgView.html',
        controller: 'imageTxtSvgController',
        transclude: true,
        link: setDomBindings
    };
}]);

控制器:

app.controller('imageTxtSvgController', ['$scope', 'imageTxtSvgService', '$filter', 'textConfigEnum', function($scope, imageTxtSvgService, $filter, textConfigEnum){

    /**
    * Returns config for each text line rect
    */
    $scope.setSvgRectanglesConfig = function(textAreaList){
        var me = this,
            numberOfElements = (textAreaList) ? textAreaList.length : 0;

        if (numberOfElements <= 0) {
            return;
        }

        $scope.svgConfig.rectConfig = imageTxtSvgService.getSvgRectListData(textAreaList, $scope.svgConfig.rectsVisible, $scope.svgConfig);
    };

    $scope.init = function(){
        // Fonts data
        $scope.textFonts = textConfigEnum.data;

        // Container for svg settings
        $scope.svgConfig = {
            text:'',
            textConfig: {
                fontFamily: $filter('getTextConfigByType')(textConfigEnum.info.Arial).fontFamily,
                fontSize: 20,
                fontDecoration: null,
                fontWeigth: null,
                fontColor:'black'
            },
            rectsVisible: true,
            rectConfig: [],

        };
    };

    $scope.init();
}]);

服务:

    app.service('imageTxtSvgService', ['$rootScope', 'svgUtilsService', function($rootScope, svgUtilsService){

    this.getSvgRectObject = function(data){
        var me = this, 
            rectObject = {
                height: 0,
                width: 0,
                fillColor: '#A8A8A8',
                outlineColor: '#A8A8A8',
                active: false
            };


        return angular.merge({}, rectObject, data);
    }

    /**
    * Handles svg text creation
    */
    this.getSvgText = function(data){
        var me = this,
            text = data.text,
            stringArray = text.split('\n');

        if(text === ""){
            return null;
        }

        return stringArray;
    },

    /**
    * Get data for rect object
    */
    this.getSvgRectData = function(textArea, isActive, prevConfig){
        var me = this,
            box = textArea.getBBox(),
            defaultRectConfig =  {
                height: box.height,
                width: box.width,
                active: isActive
            },
            rectConfig = angular.merge({}, prevConfig, defaultRectConfig);

        return me.getSvgRectObject(rectConfig);
    },

    /**
    * 
    * @returns {Array}
    */
    this.getSvgRectListData = function(textAreaList, isActive, previousConfig){
        var me = this,
            active = isActive,
            previousRectConfig,
            result = [];

        angular.forEach(textAreaList, function(textArea, index) {
            if(previousConfig.rectConfig[index]){
                previousRectConfig = previousConfig.rectConfig[index];
            }

            result.push(me.getSvgRectData(textArea, active, previousRectConfig));
        });

        return result;
    }
}]);

UPD 4:问题解决方案

看来我有办法了。

不是这个

var domText = $element.find('text'),
                textExampleList = domText,
                textConfig = $scope.svgConfig.textConfig,
                font = textConfig.fontFamily,
                size = textConfig.fontSize;

            document.fonts.load(''+size + 'px ' + font+'').then(
                function(){
                    $scope.setSvgRectanglesConfig(textExampleList);     
                }
            );

应该是这样的:

var textConfig = $scope.svgConfig.textConfig,
                font = textConfig.fontFamily,
                size = textConfig.fontSize;

            document.fonts.load(''+size + 'px ' + font+'').then(
                function(){
                    var textExampleList = $element.find('text');

                    $scope.setSvgRectanglesConfig(textExampleList);
                    $scope.$apply();        
                }
            );

我应该在 字体加载后获取 dom 数据,但在旧版本中我使用的是旧版本的 dom 数据。另外,我忘了应用更改。

【问题讨论】:

  • 你能再展示一些你的代码吗?也许是setSvgRectanglesConfig 函数?
  • @RaphaelSchweikert 当然!自动更新!
  • 已更新。尽量缩短它

标签: javascript jquery html angularjs svg


【解决方案1】:

我认为您可以为此使用Font Loading API

var text = 'Text to display';
var font = 'Font to use';
document.fonts.load('12px "'+font+'"', text).then(function() {
  // Here we can be certain the font is available
  // Measure the size now…
});

例子:

var button = document.querySelector('button');
var result = document.querySelector('div');

button.addEventListener('click', function(event) {
  var text = 'The text to rénder';
  // This loads the font (unless already available)
  document.fonts.load('12px "Baloo Paaji"', text).then(function() {
    // Here we can be certain the font is available
    result.style.fontFamily = '"Baloo Paaji"';
    result.textContent = text;
  });
}, false);
<html>

<head>
  <link href="https://fonts.googleapis.com/css?family=Baloo+Paaji" rel="stylesheet">
</head>

<body>
  <button>Load Font</button>
  <div></div>
</body>

</html>

或者,您可以尝试通过在页面中某处(隐藏)为每种字体包含一小段文本来预加载所有字体。

【讨论】:

  • 有趣。所以它发生是因为字体加载和 svg redering 发生异步?第一个解决方案看起来不错,我试试,谢谢!第二个解决方案看起来不太好._.
  • SVG 渲染不会异步发生:只要您访问显示属性,就需要进行渲染。但是,虽然字体尚未加载,但指标会有所不同,因为文本将以备用字体呈现。
  • @mzmm56 然后在我的controller 中有一个函数调用,它是更新$scope 文件,它与&lt;text ng-attr-font-family="{{scope.fontFamily}}"&gt; 之类的指令绑定
  • @RaphaelSchweikert 谢谢,拉斐尔!你的答案正是我想要的!
猜你喜欢
  • 2010-12-01
  • 2011-07-17
  • 1970-01-01
  • 1970-01-01
  • 2018-04-29
  • 1970-01-01
  • 2022-11-22
  • 1970-01-01
  • 2016-10-01
相关资源
最近更新 更多