처음에 @ font-face를 사용하여 <canvas>에 텍스트 그리기가 작동하지 않습니다.


97

@ font-face를 통해로드 된 서체로 캔버스에 텍스트를 그릴 때 텍스트가 올바르게 표시되지 않습니다. 전혀 표시되지 않거나 (Chrome 13 및 Firefox 5에서) 서체가 잘못되었습니다 (Opera 11). 이러한 유형의 예기치 않은 동작은 서체가있는 첫 번째 드로잉에서만 발생합니다. 그 후 모든 것이 잘 작동합니다.

표준 행동입니까?

감사합니다.

추신 : 다음은 테스트 케이스의 소스 코드입니다.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>@font-face and &lt;canvas&gt;</title>
        <style id="css">
@font-face {
    font-family: 'Press Start 2P';
    src: url('fonts/PressStart2P.ttf');
}
        </style>
        <style>
canvas, pre {
    border: 1px solid black;
    padding: 0 1em;
}
        </style>
    </head>
    <body>
        <h1>@font-face and &lt;canvas&gt;</h1>
        <p>
            Description: click the button several times, and you will see the problem.
            The first line won't show at all, or with a wrong typeface even if it does.
            <strong>If you have visited this page before, you may have to refresh (or reload) it.</strong>
        </p>
        <p>
            <button id="draw">#draw</button>
        </p>
        <p>
            <canvas width="250" height="250">
                Your browser does not support the CANVAS element.
                Try the latest Firefox, Google Chrome, Safari or Opera.
            </canvas>
        </p>
        <h2>@font-face</h2>
        <pre id="view-css"></pre>
        <h2>Script</h2>
        <pre id="view-script"></pre>
        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
        <script id="script">
var x = 30,
    y = 10;

$('#draw').click(function () {
    var canvas = $('canvas')[0],
        ctx = canvas.getContext('2d');
    ctx.font = '12px "Press Start 2P"';
    ctx.fillStyle = '#000';
    ctx.fillText('Hello, world!', x, y += 20);
    ctx.fillRect(x - 20, y - 10, 10, 10);
});
        </script>
        <script>
$('#view-css').text($('#css').text());
$('#view-script').text($('#script').text());
        </script>
    </body>
</html>

1
브라우저는 백그라운드에서 비동기 적으로 글꼴을로드합니다. 이것은 정상적인 동작입니다. 참조 paulirish.com/2009/fighting-the-font-face-fout
Thomas

답변:


74

캔버스에 그리기가 일어나야하고 fillText메서드 를 호출하면 즉시 돌아와야합니다 . 그러나 브라우저는 아직 백그라운드 작업 인 네트워크에서 글꼴을로드하지 않았습니다. 이 글꼴로 후퇴하는 그래서 않습니다 사용할 수 있습니다.

글꼴을 사용할 수 있는지 확인하려면 페이지에 다른 요소를 미리로드하십시오. 예 :

<div style="font-family: PressStart;">.</div>

3
을 추가 display: none하고 싶을 수 있지만 이로 인해 브라우저에서 글꼴로드를 건너 뛸 수 있습니다. 대신 공백을 사용하는 것이 좋습니다 ..
Thomas

6
공백을 사용하면 IE가 div에 있어야하는 공백 노드를 버리고 글꼴로 렌더링 할 텍스트를 남기지 않습니다. 물론 IE는 아직 캔버스를 지원하지 않기 때문에 future-IE가이 작업을 계속할 것인지, 글꼴 로딩 동작에 영향을 미칠 것인지는 알 수 없지만 오랫동안 지속 된 IE HTML 구문 분석 문제입니다.
빈스

1
글꼴을 미리로드하는 더 쉬운 방법이 없습니까? 예를 들어 어떻게 든 자바 스크립트를 통해 강제로?
조슈아

@Joshua : 페이지에 요소를 만들고 글꼴을 설정해야합니다. 위와 동일하지만 동적으로 생성됩니다.
bobince

17
이것을 추가한다고해서 JavaScript가 실행될 때 글꼴이 이미로드된다는 보장은 없습니다. 물론 문제가되는 지연된 글꼴 (setTimeout)을 사용하여 스크립트를 실행해야했습니다.
Nick

23

이 트릭을 사용 하여 onerror이벤트를 Image요소에 바인딩 하십시오 .

여기 데모 : 최신 Chrome에서 작동합니다.

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

var link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = 'http://fonts.googleapis.com/css?family=Vast+Shadow';
document.getElementsByTagName('head')[0].appendChild(link);

// Trick from /programming/2635814/
var image = new Image();
image.src = link.href;
image.onerror = function() {
    ctx.font = '50px "Vast Shadow"';
    ctx.textBaseline = 'top';
    ctx.fillText('Hello!', 20, 10);
};

3
똑똑한 속임수. 참고로 실제 글꼴 파일 (예 : .ttf, .woff 등)에 대한 참조가 포함 된 CSS를로드하고 있습니다. 나는 당신의 트릭을 두 번, 한 번은 css 파일에, 한 번은 참조 된 글꼴 파일 (.woff)에 사용하여 모든 것이로드되었는지 확인해야했습니다.
마그마

1
.ttf 글꼴로이 접근 방식을 시도했습니다. Chrome (41.0.2272.101m)에서 안정적으로 작동하지 않습니다. 5 초의 setTimeout도 도움이되지 않습니다. 첫 번째 렌더링은 기본 글꼴로 진행됩니다.
Mikhail Fiadosenka 2015 년

2
당신은 핸들러의 OnError 설정해야합니다 전에 당신이 SRC를 설정
asdjfiasd

1
또한 "새 이미지" 괄호가 없습니다.
asdjfiasd 2015 년

@asdjfiasd 괄호는 필요하지 않으며 src속성이 설정되어 있어도 다음 실행 블록에서로드가 시작됩니다.
유료 괴상한

16

문제의 핵심은 글꼴을 사용하려고하지만 브라우저가 글꼴을 아직로드하지 않았으며 요청하지 않았을 수도 있다는 것입니다. 필요한 것은 글꼴을로드하고로드 된 후 콜백을 제공하는 것입니다. 콜백을 받으면 글꼴을 사용해도된다는 것을 알 수 있습니다.

Google의 WebFont Loader를보십시오 . "맞춤형"제공 업체와active 로드 후 콜백이 작동하도록합니다.

나는 전에 그것을 사용한 적이 없지만 문서의 빠른 스캔에서 다음 fonts/pressstart2p.css과 같이 css 파일을 만들어야합니다 .

@font-face {
  font-family: 'Press Start 2P';
  font-style: normal;
  font-weight: normal;
  src: local('Press Start 2P'), url('http://lemon-factory.net/reproduce/fonts/Press Start 2P.ttf') format('ttf');
}

그런 다음 다음 JS를 추가하십시오.

  WebFontConfig = {
    custom: { families: ['Press Start 2P'],
              urls: [ 'http://lemon-factory.net/reproduce/fonts/pressstart2p.css']},
    active: function() {
      /* code to execute once all font families are loaded */
      console.log(" I sure hope my font is loaded now. ");
    }
  };
  (function() {
    var wf = document.createElement('script');
    wf.src = ('https:' == document.location.protocol ? 'https' : 'http') +
        '://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
    wf.type = 'text/javascript';
    wf.async = 'true';
    var s = document.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(wf, s);
  })();

14

캔버스에서 사용하기 전에 FontFace API로 글꼴을로드 할 수 있습니다 .

const myFont = new FontFace('My Font', 'url(https://myfont.woff2)');

myFont.load().then((font) => {
  document.fonts.add(font);

  console.log('Font loaded');
});

글꼴 리소스 myfont.woff2가 먼저 다운로드됩니다. 다운로드가 완료되면 글꼴이 문서의 FontFaceSet에 추가됩니다 .

FontFace API의 사양은이 글을 쓰는 시점의 작업 초안입니다. 여기에서 브라우저 호환성 표를 참조하십시오.


1
당신은 document.fonts.add. 브루노의 대답을보십시오.
Pacerier 2011

감사합니다 @Pacerier! 내 대답을 업데이트했습니다.
Fred Bergman 2017

이 기술은 실험적이며 대부분의 브라우저에서 지원되지 않습니다.
Michael Zelensky

11

간단한 CSS를 사용하여 다음과 같은 글꼴을 사용하여 div를 숨기는 것은 어떻습니까?

CSS :

#preloadfont {
  font-family: YourFont;
  opacity:0;
  height:0;
  width:0;
  display:inline-block;
}

HTML :

<body>
   <div id="preloadfont">.</div>
   <canvas id="yourcanvas"></canvas>
   ...
</body>



1

이것이 도움이 될지는 모르겠지만 내 코드의 문제를 해결하기 위해로드하려는 모든 글꼴을 실행하는 Javascript 상단에 for 루프를 만들었습니다. 그런 다음 캔버스를 지우고 캔버스에 원하는 항목을 미리로드하는 기능을 실행했습니다. 지금까지 완벽하게 작동했습니다. 그것이 내 코드를 아래에 게시 한 내 논리였습니다.

var fontLibrary = ["Acme","Aladin","Amarante","Belgrano","CantoraOne","Capriola","CevicheOne","Chango","ChelaOne","CherryCreamSoda",
"ConcertOne","Condiment","Damion","Devonshire","FugazOne","GermaniaOne","GorditasBold","GorditasRegular",
"KaushanScript","LeckerliOne","Lemon","LilitaOne","LuckiestGuy","Molle","MrDafoe","MrsSheppards",
"Norican","OriginalSurfer","OswaldBold","OswaldLight","OswaldRegular","Pacifico","Paprika","Playball",
"Quando","Ranchers","SansitaOne","SpicyRice","TitanOne","Yellowtail","Yesteryear"];

    for (var i=0; i < fontLibrary.length; i++) {
        context.fillText("Sample",250,50);
        context.font="34px " + fontLibrary[i];
    }

    changefontType();

    function changefontType() {
        selfonttype = $("#selfontype").val();
        inputtextgo1();
    }

    function inputtextgo1() {
        var y = 50;
        var lineHeight = 36;
        area1text = document.getElementById("bag1areatext").value;
        context.clearRect(0, 0, 500, 95)
        context.drawImage(section1backgroundimage, 0, 0);
        context.font="34px " + selfonttype;
        context.fillStyle = seltextcolor;
        context.fillText(area1text, 250, y);
    }

내 대답을 설명하기 위해 위의 코드를 추가했습니다. 다른 웹 페이지를 개발할 때 비슷한 문제가 있었는데 서버 쪽에서 모든 글꼴을로드하여 웹 페이지에 올바르게 표시 할 수 있으므로 해결되었습니다.
grapien 2013 년

1

여기에 제안 된 수정 사항의 대부분을 통합하는 jsfiddle을 작성했지만 문제가 해결되지 않았습니다. 그러나 저는 초보 프로그래머이므로 제안 된 수정 사항을 올바르게 코딩하지 않았을 수 있습니다.

http://jsfiddle.net/HatHead/GcxQ9/23/

HTML :

<!-- you need to empty your browser cache and do a hard reload EVERYTIME to test this otherwise it will appear to working when, in fact, it isn't -->

<h1>Title Font</h1>

<p>Paragraph font...</p>
<canvas id="myCanvas" width="740" height="400"></canvas>

CSS :

@import url(http://fonts.googleapis.com/css?family=Architects+Daughter);
 @import url(http://fonts.googleapis.com/css?family=Rock+Salt);
 canvas {
    font-family:'Rock Salt', 'Architects Daughter'
}
.wf-loading p {
    font-family: serif
}
.wf-inactive p {
    font-family: serif
}
.wf-active p {
    font-family:'Architects Daughter', serif;
    font-size: 24px;
    font-weight: bold;
}
.wf-loading h1 {
    font-family: serif;
    font-weight: 400;
    font-size: 42px
}
.wf-inactive h1 {
    font-family: serif;
    font-weight: 400;
    font-size: 42px
}
.wf-active h1 {
    font-family:'Rock Salt', serif;
    font-weight: 400;
    font-size: 42px;
}

JS :

// do the Google Font Loader stuff....
WebFontConfig = {
    google: {
        families: ['Architects Daughter', 'Rock Salt']
    }
};
(function () {
    var wf = document.createElement('script');
    wf.src = ('https:' == document.location.protocol ? 'https' : 'http') +
        '://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
    wf.type = 'text/javascript';
    wf.async = 'true';
    var s = document.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(wf, s);
})();

//play with the milliseconds delay to find the threshold - don't forget to empty your browser cache and do a hard reload!
setTimeout(WriteCanvasText, 0);

function WriteCanvasText() {
    // write some text to the canvas
    var canvas = document.getElementById("myCanvas");
    var context = canvas.getContext("2d");
    context.font = "normal" + " " + "normal" + " " + "bold" + " " + "42px" + " " + "Rock Salt";
    context.fillStyle = "#d50";
    context.fillText("Canvas Title", 5, 100);
    context.font = "normal" + " " + "normal" + " " + "bold" + " " + "24px" + " " + "Architects Daughter";
    context.fillText("Here is some text on the canvas...", 5, 180);
}

해결 방법 나는 결국 포기했고, 첫 번째로드에서 텍스트 이미지를 사용하는 동시에 캔버스 표시 영역 밖에 서체가있는 텍스트를 배치했습니다. 캔버스 표시 영역 내에서 이후의 모든 글꼴 표시는 문제가되지 않았습니다. 이것은 결코 우아한 해결 방법이 아닙니다.

솔루션은 내 웹 사이트에 구워졌지만 필요한 경우 jsfiddle을 만들어 보여줄 것입니다.


1

일부 브라우저CSS 글꼴로드 사양을 지원 합니다 . 모든 글꼴이로드되었을 때 콜백을 등록 할 수 있습니다. 그때까지 캔버스 그리기 (또는 적어도 캔버스에 텍스트 그리기)를 연기하고 글꼴을 사용할 수있게되면 다시 그리기를 트리거 할 수 있습니다.



0

먼저 다른 답변에서 권고 한대로 Google의 웹 글꼴 로더를 사용하고 글꼴이로드되었음을 나타 내기 위해 제공되는 콜백에 그리기 코드를 추가합니다. 그러나 이것이 이야기의 끝이 아닙니다. 이 시점부터 매우 브라우저에 따라 다릅니다. 대부분의 경우 잘 작동하지만 때로는 수백 밀리 초 동안 기다리거나 페이지의 다른 곳에서 글꼴을 사용해야 할 수도 있습니다. 나는 다른 옵션을 시도했고 afaik이 항상 작동하는 한 가지 방법은 사용할 글꼴 모음과 글꼴 크기 조합을 사용하여 캔버스에 테스트 메시지를 빠르게 그리는 것입니다. 배경과 같은 색상으로 할 수 있으므로 눈에 띄지 않고 매우 빠르게 발생합니다. 그 후 글꼴은 항상 나와 모든 브라우저에서 작동했습니다.


0

내 대답은 @ font-face가 아닌 Google 웹 글꼴을 다룹니다. 캔버스에 나타나지 않는 글꼴 문제에 대한 해결책을 모든 곳에서 검색했습니다. 타이머, setInterval, 글꼴 지연 라이브러리 및 모든 종류의 트릭을 시도했습니다. 작동하지 않았습니다. (캔버스 용 CSS 또는 캔버스 요소의 ID에 font-family를 넣는 것도 포함됩니다.)

그러나 Google 글꼴로 렌더링 된 애니메이션 텍스트가 쉽게 작동 한다는 것을 알았습니다 . 차이점이 뭐야? 캔버스 애니메이션에서는 애니메이션 항목을 반복해서 다시 그립니다. 그래서 저는 텍스트를 두 번 렌더링하는 아이디어를 얻었습니다.

짧은 (100ms) 타이머 지연을 추가하기 전까지는 작동하지 않았습니다. 지금까지 Mac에서만 테스트했습니다. Chrome은 100ms에서 잘 작동했습니다. Safari는 페이지를 다시로드해야했기 때문에 타이머를 1000으로 늘 렸는데 괜찮 았습니다. Firefox 18.0.2 및 20.0은 Google 글꼴 (애니메이션 버전 포함)을 사용하는 경우 캔버스에 아무것도로드하지 않습니다.

전체 코드 : http://www.macloo.com/examples/canvas/canvas10.html

http://www.macloo.com/examples/canvas/scripts/canvas10.js


0

같은 문제에 직면합니다. "bobince"및 기타 주석을 읽은 후 다음 자바 스크립트를 사용하여 해결합니다.

$('body').append("<div id='loadfont' style='font-family: myfont;'>.</div>");
$('#loadfont').remove();

0

새 글꼴이로드 될 때마다 (그리고 렌더링을 변경할 때마다) 다시 그리려면 글꼴로드 API에도 이에 대한 멋진 이벤트 가 있습니다. 완전한 동적 환경에서 Promise에 문제가있었습니다.

var fontFaceSet = document.fonts;
if (fontFaceSet && fontFaceSet.addEventListener) {
    fontFaceSet.addEventListener('loadingdone', function () {
        // Redraw something

    });
} else {
    // no fallback is possible without this API as a font files download can be triggered
    // at any time when a new glyph is rendered on screen
}

0

캔버스는 DOM 로딩과 독립적으로 그려집니다. 미리로드 기술은 미리로드 후에 캔버스를 그린 경우에만 작동합니다.

내 솔루션은 최고가 아니더라도 :

CSS :

.preloadFont {
    font-family: 'Audiowide', Impact, Charcoal, sans-serif, cursive;
    font-size: 0;
    position: absolute;
    visibility: hidden;
}

HTML :

<body onload="init()">
  <div class="preloadFont">.</div>
  <canvas id="yourCanvas"></canvas>
</body>

자바 스크립트 :

function init() {
    myCanvas.draw();
}


-4

아래와 같이 지연을 추가하십시오.

<script>
var c = document.getElementById('myCanvas');    
var ctx = c.getContext('2d');
setTimeout(function() {
    ctx.font = "24px 'Proxy6'"; // uninstalled @fontface font style 
    ctx.textBaseline = 'top';
    ctx.fillText('What!', 20, 10);      
}, 100); 
</script>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.