gpt4 book ai didi

javascript - html5-canvas 中的可变字体

转载 作者:行者123 更新时间:2023-12-05 04:54:38 27 4
gpt4 key购买 nike

我遇到了可变字体的问题,想知道是否有人有解决方案。我已经使用可变字体构建了这个海报生成器,您可以在其中操纵两个轴上的字体变化设置。这是一个活生生的例子 http://automat.markjulienhahn.de

现在我正在尝试通过 html2canvas 下载结果。不幸的是, Canvas 对象似乎不支持可变字体,因此 Canvas 对象只能显示字体的一种状态,而 fontVariationSettings 没有任何效果。

这就是我拉 Canvas 元素的方式:

<script src="html2canvas.min.js"></script>    

<script>

var app = new Vue({
el: '#app',
methods: {
saveCanvas(){
html2canvas(document.querySelector("#capture")).then(
canvas => {
document.body.appendChild(canvas);
var image = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
console.log(image);
window.location.href=image;
});
}
}
})

</script>

这就是我操作可变字体的方式。

function randomizeState() {
randomWeight = Math.floor(Math.random(1,100) * 100);
randomWidth = Math.floor(Math.random(1,100) * 100);
document.getElementById("element").style.fontVariationSettings = "\"frst\" " + randomWeight + ", \"scnd\" " + randomWidth;
document.getElementById("state1").innerHTML = randomWeight + " " + randomWidth;
}

如有任何帮助,我将不胜感激!

最佳答案

不幸的是你是对的,我们目前不能直接在 Canvas 中使用可变字体。所以这使得 html2canvas 的 Canvas 渲染器无法正确渲染。

新版本的 html2canvas 带有 foreignObjectRenderer ,它使用 Canvas API 的能力绘制 SVG 图像,并结合 SVG 的能力在 <foreignObject> 中包含 HTML 元素。 .

这确实是我们必须在 Canvas 上绘制可变字体的唯一当前解决方案,但是要使其正常工作,需要将字体嵌入到将在 Canvas 上绘制的 svg 文档中。而且,html2canvas 不会为我们做这件事(尽管我最近没有检查,但我认为 DOM2image 等其他解决方案也不会这样做)。

所以我们必须自己做。

  • 首先我们需要获取字体文件 (woff2) 并将其编码为 data:// URL,以便它可以存在于独立的 svg 文件中。
  • 然后我们将构建 <foreignObject>元素和我们元素的副本以及它们所需的计算样式
  • 最后,我们将使用 <foreignObject> 构建 svg 图像和一个 <style>data:// 声明我们的字体URL,并将其绘制在 Canvas 上。

(async () => {

const svgNS = "http://www.w3.org/2000/svg";
const svg = document.createElementNS( svgNS, "svg" );
const font_data = await fetchAsDataURL( "https://fonts.gstatic.com/s/inter/v2/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2" );
const style = document.createElementNS( svgNS, "style" );
style.textContent = `@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 200 900;
src: url(${ font_data }) format('woff2');
}`;
svg.append( style );

const foreignObject = document.createElementNS( svgNS, "foreignObject" );
foreignObject.setAttribute( "x", 0 );
foreignObject.setAttribute( "y", 0 );

const target = document.querySelector( ".target" );
const clone = cloneWithStyles( target );
foreignObject.append( clone );

const { width, height } = target.getBoundingClientRect();
foreignObject.setAttribute( "width", width );
foreignObject.setAttribute( "height", height );
svg.setAttribute( "width", width );
svg.setAttribute( "height", height );

svg.append( foreignObject );

const svg_markup = new XMLSerializer().serializeToString( svg );
const svg_file = new Blob( [ svg_markup ], { type: "image/svg+xml" } );

const img = new Image();
img.src = URL.createObjectURL( svg_file );
await img.decode();
URL.revokeObjectURL( img.src );

const canvas = document.createElement( "canvas" );
Object.assign( canvas, { width, height } );
const ctx = canvas.getContext( "2d" );
ctx.drawImage( img, 0, 0 );

document.body.append( canvas );

})().catch( console.error );


function fetchAsDataURL( url ) {
return fetch( url )
.then( (resp) => resp.ok && resp.blob() )
.then( (blob) => new Promise( (res) => {
const reader = new FileReader();
reader.onload = (evt) => res( reader.result );
reader.readAsDataURL( blob );
} )
);
}
function cloneWithStyles( source ) {
const clone = source.cloneNode( true );

// to make the list of rules smaller we try to append the clone element in an iframe
const iframe = document.createElement( "iframe" );
document.body.append( iframe );
// if we are in a sandboxed context it may be null
if( iframe.contentDocument ) {
iframe.contentDocument.body.append( clone );
}

const source_walker = document.createTreeWalker( source, NodeFilter.SHOW_ELEMENT, null );
const clone_walker = document.createTreeWalker( clone, NodeFilter.SHOW_ELEMENT, null );
let source_element = source_walker.currentNode;
let clone_element = clone_walker.currentNode;
while ( source_element ) {

const source_styles = getComputedStyle( source_element );
const clone_styles = getComputedStyle( clone_element );

// we should be able to simply do [ ...source_styles.forEach( (key) => ...
// but thanks to https://crbug.com/1073573
// we have to filter all the snake keys from enumerable properties...
const keys = (() => {
// Start with a set to avoid duplicates
const props = new Set();
for( let prop in source_styles ) {
// Undo camel case
prop = prop.replace( /[A-Z]/g, (m) => "-" + m.toLowerCase() );
// Fix vendor prefix
prop = prop.replace( /^webkit-/, "-webkit-" );
props.add( prop );
}
return props;
})();
for( let key of keys ) {
if( clone_styles[ key ] !== source_styles[ key ] ) {
clone_element.style.setProperty( key, source_styles[ key ] );
}
}

source_element = source_walker.nextNode()
clone_element = clone_walker.nextNode()

}
// clean up
iframe.remove();

return clone;
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 200 900;
src: url(https://fonts.gstatic.com/s/inter/v2/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) format('woff2');
}

.t1 {
font-family: 'Inter';
font-variation-settings: 'wght' 200;
}
.t2 {
font-family: 'Inter';
font-variation-settings: 'wght' 900;
}

canvas {
border: 1px solid;
}
<div class="target">
<span class="t1">
Hello
</span>
<span class="t2">
World
</span>
</div>

关于javascript - html5-canvas 中的可变字体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65670959/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com