【问题标题】:Compare two HTML elements view using jquery (and | or) with Canvas使用 jquery(和 | 或)与 Canvas 比较两个 HTML 元素视图
【发布时间】:2013-04-26 06:19:29
【问题描述】:

我有如下文本 DIV。我只针对非 IE 和 webkit 浏览器。

一个:

<div id="target1"> Some<sup>en</sup> <i><b>text</b></i> </div>

上面的行看起来像:Someentext with style

第二:

<div id="target2"> Some<sup>en</sup> <span class='boldIt'>text</span></div>
.boldIt{
   font-weight : bold;
   font-style : italic;
}

看起来和上面一样。根据观点两者是相同的。 但是下面和上面不一样

<div id="target3"> Someen text</div>

我的要求是比较三个目标元素。

我尝试过的: 我尝试使用getComputedStyle() 遍历DIV 的每个子节点并匹配样式。但是我可以看到页面上发生了很大的性能问题。因为涉及的案例太多了。

有想法,无奈,这里有个IDEA 在画布上绘制两个元素并比较两个画布,就像比较两个图像一样。这真的可能吗?如果可能,当我将一个内联元素与块级元素进行比较时,它会改变比较的行为吗?

请就此向我提出建议。如果没有想法,我会坚持使用我之前提到的 HTML 方式。

我已经尝试使用画布this,我的目标只是 Chrome。安东尼的回答完全符合我的要求。但在下面的情况下,我找不到办法做到这一点

for(var i = 0; i < arr.length; i++){
  var htStr = arr[i].one, htStr2 = arr[i].two;
  // How can I match these two now...
} 

【问题讨论】:

  • 为什么需要比较它们?
  • 这就像尝试做一些我感兴趣的事情并学习一些东西:)
  • 比较 HTML 字符串是否被视为一种选择?
  • 那么,在您的示例中,您是否认为 target1 和 target2 相同?
  • @dsfq 样式可以通过多种方式给出,真的可以比较字符串来验证吗?

标签: javascript jquery webkit html5-canvas


【解决方案1】:

您使用&lt;canvas&gt; 的想法是可能的。

参见 DEMO(在 Chrome、Safari 和 Opera 上测试)。

您可以使用html2canvas 将以下内容转换为 3 个不同的画布,并比较它们的 base64 字符串以确定它们在视觉上是否相同。您应该将它们包装在单独的容器中,以便所有文本都呈现在画布中。

<div id="target1-container">
    <div id="target1"> Some<sup>en</sup> <i><b>text</b></i> </div>
</div>
<div id="target2-container">
    <div id="target2"> Some<sup>en</sup> <span class='boldIt'>text</span></div>
</div>
<div id="target3-container">
    <div id="target3"> Someen text</div>
</div>

转换为canvas和base64字符串,使用async.js处理多个异步调用:

async.parallel({
    target1: function (callback) {
        html2canvas(document.getElementById("target1-container"), {
            onrendered: function (canvas) {
                callback(null, canvas.toDataURL());
            }
        });
    },
    target2: function (callback) {
        html2canvas(document.getElementById("target2-container"), {
            onrendered: function (canvas) {
                callback(null, canvas.toDataURL());
            }
        });
    },
    target3: function (callback) {
        html2canvas(document.getElementById("target3-container"), {
            onrendered: function (canvas) {
                callback(null, canvas.toDataURL());
            }
        });
    }
},
function (err, results) {
    console.log(results);
});

关于您更新的问题,这是一个比较由字符串定义的元素的视觉外观的函数:

function compare(arr, fn) {
    // create test div
    var testdiv = document.createElement("div");
    testdiv.style.marginTop = '1000px';
    document.body.appendChild(testdiv);
    async.map(
        arr,
        function (data, callback) {
            var htStr = data.one,
                htStr2 = data.two;
            // create elements from strings
            var el = document.createElement("div"),
                el2 = document.createElement("div");
            el.innerHTML = htStr;
            testdiv.appendChild(el);
            el2.innerHTML = htStr2;
            testdiv.appendChild(el2);
            // convert to canvas and base64 strings
            async.parallel([
                function (callback) {
                    html2canvas(el, {
                        onrendered: function (canvas) {
                            callback(null, canvas.toDataURL());
                        }
                    });
                },
                function (callback) {
                    html2canvas(el2, {
                        onrendered: function (canvas) {
                            callback(null, canvas.toDataURL());
                        }
                    });
                }
            ],
            // start comparison
            function (err, results) {
                if (results[0] === results[1]) {
                    callback(null, true);
                } else {
                    callback(null, false);
                }
            });
        },
        // callback function
        function (err, results) {
            document.body.removeChild(testdiv);
            if (typeof fn === 'function') {
                fn(results);
            }
        }
    );
}
// elements to be tested
var arr = [];
arr.push({
    one: '<div id="target1"> Some<sup>en</sup> <i><b>text</b></i> </div>',
    two: '<div id="target2"> Some<sup>en</sup> <span class="boldIt">text</span></div>'
});
arr.push({
    one: '<div id="target1"> Some<sup>en</sup> <i><b>text</b></i> </div>',
    two: '<div id="target3"> Someen text</div>'
});
// let the test begin
compare(arr, function (result) {
    console.log(result);
});

它在回调函数中返回一个数组,带有

  • true 表示元素在视觉上是相同的,并且
  • false 表示他们不同。

this DEMO 中查看控制台。

【讨论】:

  • @Antony for me 在 chrome26 win7 下为 target1 和 target2 提供了两个不同的 base64 图像
  • @Antony 我已经尝试过您的解决方案,它适用于 i、sup、sub、u 标签,因为画布中会有像素级别的差异。但是,如果一个文本有粗体而另一个文本没有粗体,则它会失败。请在此处查看jsfiddle.net/xJ52P/5。非常感谢您的帮助。
  • @Exception 已修复。看来test div 无法向左或向右移出屏幕。我现在把它推到页面的最底部。见jsfiddle.net/xJ52P/6
  • @Antony 需要一个小建议。而不是只记录真或假,我想显示el或'el2'的文本在视觉上不匹配......但在代码中我可以点头理解在哪里更新自定义错误消息并使用适当的评论更好结果。
  • @Exception 我使用turefalse 的原因是为了更容易使用结果。您不必修改函数,只需更改回调函数即可。见jsfiddle.net/xJ52P/9
【解决方案2】:

我假设“比较”是指检测元素是否在像素级别以相同的方式呈现。请注意,在词汇基础上完全不相等的元素可以满足此条件;相反,两个元素可能在词汇上相同,但由于许多因素而呈现不同,特别是从父母那里继承的属性。您还需要考虑这样一个事实,即元素的像素表示在构成其包含矩形的像素的意义上可能会被页面上其他地方的东西污染;最明显的例子是位置固定的元素。

鉴于以上所有情况,既然我们将等价定义为像素级等价,那么答案确实是对要比较的元素进行“快照”并进行比较。最简单的方法是使用无头 Web 浏览器,例如 PhantomJS。我不会提供完整的程序,但这里是基础知识:

  1. 在您的page.evaluate 函数中,使用getBoundingClientRect 获取相关元素的尺寸。

  2. 然后使用window.callPhantom 回调 PhantomJS。这将调用使用page.onCallBack 定义的函数。

  3. 在回调中,将page.clipRect 设置为您要捕获的项目的尺寸。然后使用page.renderBase64 之类的东西来捕获图像,并将结果存储为文件。

  4. 重复您要比较的其他元素。

  5. 比较存储的 base64 图像的字节级等效性。

【讨论】:

  • 感谢您的提示。但我正在寻找纯 JS 解决方案,因为我将在 Chrome 扩展程序中使用它。
  • 那么,就这么说吧。我怀疑通过查看 HTML/CSS 来确定两个元素是否呈现相同是一个 NP 完全问题。您必须渲染和比较像素。您不能在带有 JS 的浏览器中执行此操作。您将竭尽全力尝试将 HTML 渲染到画布上。但是请参阅stackoverflow.com/questions/4912092/…,我假设您在发布问题之前已经对其进行了审查。请注意,执行此操作的工具可用于扩展,这从许多这样做的扩展中显而易见。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多