Hello.

I am Paul Kinlan.

A Developer Advocate for Chrome and the Open Web at Google.

Puppeteer Go🔗

Paul Kinlan

Ich liebe Puppenspieler - hier kann ich mit den Ideen von The Headless Web - der das Web in einem Browser ohne sichtbaren Browser DOM-curl und sogar Tools wie DOM-curl (Curl, der JavaScript DOM-curl ) erstellt. Ich liebe es, Skripte für den Browser zu erstellen, um Seiten zu bearbeiten und mit ihnen zu interagieren.

Eine Demo, die ich machen wollte, wurde von Capturing 422 live images Post inspiriert, in dem sie ein Puppenspielskript Capturing 422 live images , das zu vielen Seiten navigierte und einen Screenshot machte. Anstatt auf viele Seiten zu gehen, wollte ich viele Screenshots von Elementen auf der Seite machen.

Das Problem, das ich mit Puppeteer habe, ist die Eröffnungs-Strophe, die Sie brauchen, um etwas zu tun. Starten, Tab öffnen, navigieren - es ist nicht komplex, es ist nur mehr Boilerplate, als ich für einfache Skripte erstellen möchte. Deshalb habe ich Puppeteer Go . Es ist nur ein kleines Skript, mit dem ich auf einfache Weise CLI-Dienstprogramme erstellen kann. Es öffnet den Browser, navigiert zu einer Seite, führt Ihre Aktion aus und bereinigt sich anschließend von selbst.

Hör zu.

const { go } = require('puppeteer-go');

go('https://paul.kinlan.me', async (page) => {
    const elements = await page.$$("h1");
    let count = 0;
    for(let element of elements) {
      try {
        await element.screenshot({ path: `${count++}.png`});
      } catch (err) {
        console.log(count, err);
      }
    }
});

Der obige Code findet das Element h1 in meinem Blog und macht einen Screenshot. Dies ist bei weitem nicht so gut wie die Arbeit von Ire, aber ich fand es klasse zu sehen, ob wir Screenshots von canisuse.com schnell direkt von der Seite ziehen können.

const { go } = require('puppeteer-go');

go('https://caniuse.com/#search=css', async (page) => {
    const elements = await page.$$("article.feature-block.feature-block--feature");
    let count = 0;
    for(let element of elements) {
      try {
        await element.screenshot({ path: `${count++}.png`});
      } catch (err) {
        console.log(count, err);
      }
    }
});
4.png
3.png
2.png
1.png
0.png

Genießen!

About Me: Paul Kinlan

I lead the Chrome Developer Relations team at Google.

We want people to have the best experience possible on the web without having to install a native app or produce content in a walled garden.

Our team tries to make it easier for developers to build on the web by supporting every Chrome release, creating great content to support developers on web.dev, contributing to MDN, helping to improve browser compatibility, and some of the best developer tools like Lighthouse, Workbox, Squoosh to name just a few.

A simple video insertion tool for EditorJS🔗

Paul Kinlan

Ich mag EditorJS wirklich. Damit kann ich eine sehr einfache, im Web gehostete Oberfläche für meinen statischen Hugo-Blog erstellen.

EditorJS hat das meiste, was ich in einem einfachen blockbasierten Editor brauche. Es verfügt über ein Plugin für Header, Code und sogar eine einfache Möglichkeit, Bilder zum Editor hinzuzufügen, ohne dass eine Hosting-Infrastruktur erforderlich ist. Bisher gibt es keine einfache Möglichkeit, Videos zum Editor hinzuzufügen.

Ich nahm das simple-image Plugin-Repository und änderte es (nur ein bisschen), um ein simple-video Plugin ( npm module ) zu erstellen. Jetzt kann ich problemlos Videos in diesen Blog einbinden.

Wenn Sie mit EditorJS vertraut sind, ist es ziemlich einfach, es in Ihre Projekte aufzunehmen. Installieren Sie es einfach wie folgt

npm i simple-video-editorjs

Und fügen Sie es dann einfach in Ihr Projekt ein, wie Sie es für richtig halten.

const SimpleVideo = require('simple-video-editorjs');

var editor = EditorJS({
  ...
  
  tools: {
    ...
    video: SimpleVideo,
  }
  
  ...
});

Der Editor verfügt über einige einfache Optionen, mit denen Sie konfigurieren können, wie das Video auf der Seite gehostet werden soll:

  1. Autoplay - Das Video wird automatisch abgespielt, wenn die Seite geladen wird
  2. stummgeschaltet - hat das Video standardmäßig keinen Ton (für die automatische Wiedergabe erforderlich)
  3. Steuerelemente - Verfügt das Video über die Standard-HTML-Steuerelemente?

Im Folgenden finden Sie ein kurzes Beispiel für ein Video, das eingebettet ist (und einige der Optionen zeigt).

Wie auch immer, ich hatte Spaß beim Erstellen dieses kleinen Plugins - es war nicht allzu schwer zu erstellen und das einzige, was ich tat, war, die Konvertierung auf base64 zu verschieben, die simple-images verwendet, und stattdessen einfach die Blob-URLs zu verwenden.

Test post Video upload

Friendly Project Name Generator with Zeit🔗

Paul Kinlan

Ich habe einige Ideen für Projekte, die das Erstellen von Websites im Web erleichtern. Eine der Ideen ist, netlify-like drag and drop interface für zeit basierte Projekte zu zeit (ich mag zeit, aber die Bereitstellung erfordert ein kleines bisschen Cli-Magie).

Dieser Beitrag behandelt nur einen kleinen Teil des Puzzles: das Erstellen von Projektnamen.

Glitch ist ein gutes Beispiel dafür. Wenn Sie ein Projekt erstellen, erhält es einen skurrilen, zufällig generierten Namen. Das Team hat auch ein good dictionary of fairly safe words , das sich gut kombinieren lässt (und wenn Sie möchten, dass es einen einfachen Server zum good dictionary of fairly safe words ).

Also bestand das Nebenprojekt an diesem Sonntag darin, einen einfachen serverless-functions zu erstellen, mit dem mithilfe von Zeits serverless-functions und dem Wörterbuch von Glitch zufällige Projektnamen generiert werden können.

And here it is ( code ), es ist ziemlich kurz und nicht zu komplex.

const words = require("friendly-words");

function generate(count = 1, separator = "-") {
  const { predicates, objects } = words;
  const pCount = predicates.length;
  const oCount = objects.length;
  const output = [];

  for (let i = 0; i < count; i++) {
    const pair = [predicates[Math.floor(Math.random() * pCount)], objects[Math.floor(Math.random() * oCount)]];
    output.push(pair.join(separator));
  }

  return output;
}

module.exports = { generate }

Wenn Sie es nicht direkt in Ihr Projekt aufnehmen möchten, können Sie mithilfe des HTTP-Endpunkts zufällige Projektnamen (in Form von "XY") generieren, indem Sie eine Webanforderung an https: // friendly-project-name senden. kinlan.now.sh/api/names, was ungefähr Folgendes zurückgibt.

["momentous-professor"]

Sie können auch steuern, wie viele Namen mit dem Abfragezeichenfolgenparameter count = x generiert werden sollen, z. B. https://friendly-project-name.kinlan.now.sh/api/names?count=100

["melon-tangerine","broad-jury","rebel-hardcover","far-friend","notch-hornet","principled-wildcat","level-pilot","steadfast-bovid","holistic-plant","expensive-ulna","sixth-gear","political-wrench","marred-spatula","aware-weaver","awake-pair","nosy-hub","absorbing-petunia","rhetorical-birth","paint-sprint","stripe-reward","fine-guardian","coconut-jumbo","spangle-eye","sudden-euphonium","familiar-fossa","third-seaplane","workable-cough","hot-light","diligent-ceratonykus","literate-cobalt","tranquil-sandalwood","alabaster-pest","sage-detail","mousy-diascia","burly-food","fern-pie","confusion-capybara","harsh-asterisk","simple-triangle","brindle-collard","destiny-poppy","power-globeflower","ruby-crush","absorbed-trollius","meadow-blackberry","fierce-zipper","coal-mailbox","sponge-language","snow-lawyer","adjoining-bramble","deserted-flower","able-tortoise","equatorial-bugle","neat-evergreen","pointy-quart","occipital-tax","balsam-fork","dear-fairy","polished-produce","darkened-gondola","sugar-pantry","broad-slouch","safe-cormorant","foregoing-ostrich","quasar-mailman","glittery-marble","abalone-titanosaurus","descriptive-arch","nickel-ostrich","historical-candy","mire-mistake","painted-eater","pineapple-sassafras","pastoral-thief","holy-waterlily","mewing-humor","bubbly-cave","pepper-situation","nosy-colony","sprout-aries","cyan-bestseller","humorous-plywood","heavy-beauty","spiral-riverbed","gifted-income","lead-kiwi","pointed-catshark","ninth-ocean","purple-toucan","tundra-cut","coal-geography","icy-lunaria","agate-wildcat","respected-garlic","polar-almandine","periodic-narcissus","carbonated-waiter","lavish-breadfruit","confirmed-brand","repeated-period"]

Sie können das Trennzeichen mit dem Abfragezeichenfolgenparameter separator steuern. dh separator = @, zB https://friendly-project-name.kinlan.now.sh/api/names?separator=@

["handsomely@asterisk"]

Ein sehr nützlicher Aspekt dieses Projekts ist, dass es einfach ist, das Glitch-Repo zu aktualisieren, um sicherzustellen, dass es nicht wieder vorkommt, wenn eine Kombination von Wörtern dazu neigt, anstößig zu sein.

Unter der Annahme, dass das Hosting des Projekts nicht zu teuer wird, werde ich den Service aufrechterhalten, aber Sie können ihn auch selbst klonen, wenn Sie jemals einen ähnlichen Mikroservice erstellen möchten.

Live Beispiel

Was folgt, ist ein superschnelles Beispiel für die API in Aktion.

const render = (promise, elementId) => {
  promise.then(async(response) => {
    const el = document.getElementById(elementId);
    el.innerText = await response.text();
  })
};


onload = () => {
  render(fetch("https://friendly-project-name.kinlan.now.sh/api/names"), "basic");
  render(fetch("https://friendly-project-name.kinlan.now.sh/api/names?count=100"), "many");
  render(fetch("https://friendly-project-name.kinlan.now.sh/api/names?separator=@"), "separator");
}


Viele Antworten


Benutzerdefinierte Trennzeichen


{{<raw-html>}}

{{</ raw-html>}}

Frankie and Bennys: Pay for your meal via the web

Paul Kinlan

Immer wenn ein Restaurant sagt, dass Sie mobil bezahlen können, schaue ich es mir an, meistens, um zu beklagen, dass Sie eine App benötigen. Stellen Sie sich meine Überraschung vor, als der QR-Code zu einem webbasierten Zahlungsfluss führte ... und es funktionierte. Tolle Arbeit, Frankie und Benny's! Zu diesem Zeitpunkt habe ich Google Pay ausgewählt, aber es hat nicht funktioniert (E-Mail intern gesendet!) Ziemlich tolles Zeug, und es war ungefähr eine Minute von Ende zu Ende.

Read More

Podroll

Adding "dark mode" to my blog

Paul Kinlan

Ich habe Jeremy Keith's post about adding dark mode to his blog und es schien einfach zu sein, also habe ich beschlossen, es auszuprobieren. Hier ist das diff of the work für alle zu sehen. Es war überraschend einfach (abgesehen von albernen Fehlern meinerseits). Es gab einen kleinen Refaktor zur Unterstützung von CSS-Variablen und um sicherzustellen, dass ich einen Fallback habe, wenn es einen Browser gibt, der keine benutzerdefinierten CSS-Eigenschaften unterstützt.

Read More

Using Web Mentions in a static site (Hugo)

Paul Kinlan

Mein Blog ist eine komplett statische Seite, die mit Hugo erstellt und mit Zeit gehostet wurde. Dies ist eine großartige Lösung für mich. Ein einfaches Blog hat einen ziemlich einfachen Bereitstellungsprozess und wird blitzschnell geladen. Statisch generierte Websites haben einige Nachteile. Die größte ist, wenn Sie etwas Dynamisches benötigen, das in Ihre Seite integriert werden soll (z. B. Kommentare). Wenn Sie dynamische Inhalte nicht einfach hosten können, verlassen Sie sich auf JavaScript von Drittanbietern, das dann uneingeschränkten Zugriff auf Ihre Seite erhält und Sie wissen nicht, was es tut - es kann sein, dass Ihre Benutzer verfolgt werden oder Ihre Seite verlangsamt wird Belastung.

Read More

Creating a pop-out iframe with adoptNode and "magic iframes"

Paul Kinlan

Update: 8. Oktober - Wichtige Probleme mit diesem Dokument. Ich habe mit Jake Archibald über diesen Beitrag gesprochen, weil ich dachte, ich hätte etwas Neues. Während des Gesprächs haben wir viele Dinge aufgedeckt, die einen Teil dieses Beitrags ungültig machen, und ich habe dabei auch eine Menge gelernt, von denen ich glaube, dass die meisten Entwickler das nicht tun kennt. Durch Aufrufen von .append() und .appendChild() der Knoten übernommen. Dies macht die Verwendung von adoptNode in dieser Instanz unbrauchbar, da der Append-Algorithmus sicherstellt, dass der Knoten übernommen wird.

Read More

Meatspace Augmented Reality: From Chester to Nagoya

Paul Kinlan

Some thoughts on AR after finding some during my travels. TL;DR - cheaper content creation and better discovery tools are needed.

Read More

Photos from Carlisle Castle

Paul Kinlan

Ich war vor kurzem mit den Jungs im Urlaub und wir Carlisle Castle an Carlisle Castle (dem Geburtsort des weltbekannten Jake Archibald) vorbei. Es ist eines der besseren Schlösser, in denen wir in Großbritannien waren, und ich würde wärmstens empfehlen, etwas Zeit damit zu verbringen, es zu besuchen, wenn Sie in der Gegend sind. Ich wusste nicht, wie wichtig die Rolle war, die Carlisle Castle in der Geschichte Englands und Schottlands spielte, und es war großartig, mehr darüber herauszufinden, während wir dort waren.

Read More

Idle observation: Indexing text in images

Paul Kinlan

Ich war neulich mit den Jungs in Llangollen unterwegs (es ist eine wunderschöne Stadt) und machte Fotos von den Hinweisschildern, die einen Teil der Geschichte der Gegend enthielten, damit ich sie später lesen konnte, und dachte, ich würde zuschauen im Internet, um zu sehen, ob die Informationen für mehr als nur die Personen verfügbar waren, die am Schild vorbeigingen - und das ist nicht der Fall. Ich habe mir dann Gedanken über die Flugblätter gemacht, deren Inhalt im Internet kaum zu finden ist.

Read More

Liverpool World Museum

Paul Kinlan

Ich habe die Kinder neulich ins Liverpool World History Museum gebracht, es war ziemlich ordentlich. Der Bereich Raum und Zeit hat sich in etwa 30 Jahren nicht verändert, ein großer Teil des Käfergeheges wurde geschlossen, und das Aquarium schien ein bisschen kleiner zu sein, als ich mich erinnere. Die ägyptische Abteilung war geöffnet (es war nicht, als ich das letzte Mal ging) und es war ziemlich toll.

Read More

Bookstore - Llangollen

Paul Kinlan

Ich liebe diesen Ort, er befindet sich auf einem Café in Llangollen. Ich bin vor fast 30 Jahren mit meinen Großeltern hierher gekommen, und jetzt ist es so ziemlich dasselbe. Mein einziger Wunsch ist, dass es noch mehr Commix-Bücher gibt - ich schwöre, als ich ein Kind war, gab es noch einen Haufen mehr. Check it out

Read More

Webmention.app🔗

Paul Kinlan

Ich liebe die Idee von Webmentions , aber ich hatte nicht die Zeit, sie auf meiner Website umzusetzen. Bei allgemeinen Web-Erwähnungen können Sie andere Inhalte im Web kommentieren, mögen und darauf antworten und sie für diese Inhalte sichtbar machen, ohne mit Tools wie Disqus (die ich unbedingt von meiner Website entfernen möchte) zentralisiert zu werden.

Web-Erwähnungen sind in zwei Komponenten unterteilt, den Absender und den Empfänger. Der Empfänger ist die Site, über die ich einen Beitrag schreibe, und sie haben möglicherweise etwas auf ihrer Site, das eingehende Links oder Reaktionen auf ihren Blog anzeigt. und der Absender ist gut ich. Ich muss die entfernte Site, die ich geschrieben oder auf einen von ihnen erstellten Inhalt reagiert habe, freigeben.

Die ziemlich genial Remy Sharp erstellt webmention.app einen Teil des Problems zu lösen: Senden Pings. Mit Remys Tool können Sie ganz einfach "Pings" an potenzielle Empfänger senden, mit denen ich verbunden bin, indem Sie einfach ein CLI-Skript aufrufen.

Ich habe mein Blog mit Zeit unter Verwendung von Hugo und dem Static-Builder-Tool relatively trivial for me to add in support for webmention app , also war es relatively trivial for me to add in support for webmention app . Ich habe nur npm i webmention und rufe dann die CLI-Version des Tools aus meiner build.sh Datei auf - es ist wirklich so einfach.

Wenn ich jetzt einen Beitrag erstelle, sollte er einen kurzen Ping an alle neuen URLs senden, die ich zu ihrer Website erstellt habe.

Creating a commit with multiple files to Github with JS on the web

Paul Kinlan

Meine Seite ist entirely static . Es wurde mit Hugo und mit Zeit gehostet. Ich bin ziemlich zufrieden mit dem Setup, erhalte beinahe sofortige Builds und eine superschnelle Lieferung von CDN-Inhalten und kann all die Dinge tun, die ich brauche, weil ich keinen Zustand verwalten muss. Ich habe ein simple UI für diese Site und auch mein podcast creator , mit dem ich schnell neue Inhalte auf meiner statisch gehosteten Site veröffentlichen kann.

Read More

Screen Recorder: recording microphone and the desktop audio at the same time🔗

Paul Kinlan

Ich habe mir zum Ziel gesetzt, die einfachste Bildschirmaufzeichnungssoftware der Welt zu entwickeln, und habe in den letzten Monaten langsam an dem Projekt herumgeschraubt (ich meine wirklich langsam).

In früheren Beiträgen hatte ich das screen recording and a voice overlay indem ich mit den Streams aus allen screen recording and a voice overlay herumgebastelt hatte. Ein Bereich der Frustration war jedoch, dass ich nicht herausfinden konnte, wie ich das Audio vom Desktop * bekommen und * das Audio vom Lautsprecher überlagern kann. Ich habe endlich herausgefunden, wie es geht.

Erstens erlaubt getDisplayMedia in Chrome jetzt die getDisplayMedia Audiodaten. Es scheint ein seltsames Versehen in der Spezifikation zu geben, dass Sie audio: true im Funktionsaufruf angeben audio: true Jetzt können Sie es.

const audio = audioToggle.checked || false;
desktopStream = await navigator.mediaDevices.getDisplayMedia({ video:true, audio: audio });

Zweitens hatte ich ursprünglich gedacht, dass ich durch das Erstellen von zwei Spuren im Audiostream das bekommen würde, was ich wollte, aber ich habe erfahren, dass die MediaRecorder API von Chrome nur eine Spur MediaRecorder kann, und MediaRecorder hätte es sowieso nicht funktioniert, weil Spuren Wie bei der DVD gibt es mehrere Audiospuren, die jeweils nur von einer abgespielt werden können.

Die Lösung ist wahrscheinlich für viele Menschen einfach, aber für mich war sie neu: Verwenden Sie Web-Audio.

Es stellt sich heraus, dass die WebAudio-API über createMediaStreamSource und createMediaStreamDestination verfügt. Beide APIs werden zur Lösung des Problems benötigt. Das createMediaStreamSource kann Streams von meinem Desktop-Audio und meinem Mikrofon createMediaStreamSource Durch Verbinden der beiden Streams zu dem von createMediaStreamDestination erstellten createMediaStreamDestination kann ich diesen einen Stream in die MediaRecorder API MediaRecorder .

const mergeAudioStreams = (desktopStream, voiceStream) => {
  const context = new AudioContext();
    
  // Create a couple of sources
  const source1 = context.createMediaStreamSource(desktopStream);
  const source2 = context.createMediaStreamSource(voiceStream);
  const destination = context.createMediaStreamDestination();
  
  const desktopGain = context.createGain();
  const voiceGain = context.createGain();
    
  desktopGain.gain.value = 0.7;
  voiceGain.gain.value = 0.7;
   
  source1.connect(desktopGain).connect(destination);
  // Connect source2
  source2.connect(voiceGain).connect(destination);
    
  return destination.stream.getAudioTracks();
};

Simples.

Den vollständigen Code finden Sie auf my glitch . Die Demo finden Sie hier: https://screen-record-voice.glitch.me/

{{<fast-youtube oGIdqcMFKlA>}}

Extracting text from an image: Experiments with Shape Detection🔗

Paul Kinlan

Ich hatte nach Google IO eine kurze Ausfallzeit und wollte einen langfristigen Juckreiz bekommen, den ich hatte. Ich möchte nur Text kopieren können, der in Bildern im Browser enthalten ist. Das ist alles. Ich denke, es wäre eine nette Sache für alle.

Es ist nicht einfach, Funktionen direkt in Chrome einzufügen, aber ich weiß, dass ich das Intent-System unter Android nutzen kann und das jetzt über das Web (oder zumindest Chrome unter Android).

Zwei neue Ergänzungen zur Webplattform - Share Target Level 2 (oder wie ich es gerne nenne File Share) und TextDetector in der Shape Detection API - have allowed me to build a utility that I can Share images to and get the text held inside them .

Die grundlegende Implementierung ist relativ unkompliziert. Sie erstellen ein Freigabeziel und einen Handler im Service Worker. Sobald Sie das vom Benutzer freigegebene Image haben, führen Sie TextDetector darauf aus.

Mit Share Target API kann Ihre Webanwendung Teil des Subsystems für die native Freigabe sein. In diesem Fall können Sie sich jetzt für die Verarbeitung aller image/* Typen registrieren, indem Sie diese in Web App Manifest wie folgt deklarieren.

"share_target": {
  "action": "/index.html",
  "method": "POST",
  "enctype": "multipart/form-data",
  "params": {
    "files": [
      {
        "name": "file",
        "accept": ["image/*"]
      }
    ]
  }
}

Wenn Ihr PWA installiert ist, sehen Sie es an allen Orten, an denen Sie Bilder freigeben, wie folgt:

Die Share Target API behandelt das Share Target Dateien wie einen Formularbeitrag. Wenn die Datei für die Web-App freigegeben wird, wird der Service-Worker aktiviert und der fetch Handler mit den fetch aufgerufen. Die Daten befinden sich jetzt im Service Worker, aber ich benötige sie im aktuellen Fenster, damit ich sie verarbeiten kann. Der Service weiß, in welchem Fenster die Anforderung aufgerufen wurde, sodass Sie auf einfache Weise auf den Client zugreifen und ihm die Daten senden können.

self.addEventListener('fetch', event => {
  if (event.request.method === 'POST') {
    event.respondWith(Response.redirect('/index.html'));
    event.waitUntil(async function () {
      const data = await event.request.formData();
      const client = await self.clients.get(event.resultingClientId || event.clientId);
      const file = data.get('file');
      client.postMessage({ file, action: 'load-image' });
    }());
    
    return;
  }
  ...
  ...
}

Sobald sich das Bild in der Benutzeroberfläche befindet, verarbeite ich es mit der Texterkennungs-API.

navigator.serviceWorker.onmessage = (event) => {  
  const file = event.data.file;
  const imgEl = document.getElementById('img');
  const outputEl = document.getElementById('output');
  const objUrl = URL.createObjectURL(file);
  imgEl.src = objUrl;
  imgEl.onload = () => {
    const texts = await textDetector.detect(imgEl);
    texts.forEach(text => {
      const textEl = document.createElement('p');
      textEl.textContent = text.rawValue;
      outputEl.appendChild(textEl);
    });
  };
  ...
};

Das größte Problem ist, dass der Browser das Bild nicht auf natürliche Weise dreht (siehe unten), und für die Formerkennungs-API muss sich der Text in der richtigen Leserichtung befinden.

Ich habe das recht einfach zu verwendende EXIF-Js library , um die Drehung zu erkennen und dann einige grundlegende Canvas-Manipulationen EXIF-Js library , um das Bild neu EXIF-Js library .

EXIF.getData(imgEl, async function() {
  // http://sylvana.net/jpegcrop/exif_orientation.html
  const orientation = EXIF.getTag(this, 'Orientation');
  const [width, height] = (orientation > 4) 
                  ? [ imgEl.naturalWidth, imgEl.naturalHeight ]
                  : [ imgEl.naturalHeight, imgEl.naturalWidth ];

  canvas.width = width;
  canvas.height = height;
  const context = canvas.getContext('2d');
  // We have to get the correct orientation for the image
  // See also https://stackoverflow.com/questions/20600800/js-client-side-exif-orientation-rotate-and-mirror-jpeg-images
  switch(orientation) {
    case 2: context.transform(-1, 0, 0, 1, width, 0); break;
    case 3: context.transform(-1, 0, 0, -1, width, height); break;
    case 4: context.transform(1, 0, 0, -1, 0, height); break;
    case 5: context.transform(0, 1, 1, 0, 0, 0); break;
    case 6: context.transform(0, 1, -1, 0, height, 0); break;
    case 7: context.transform(0, -1, -1, 0, height, width); break;
    case 8: context.transform(0, -1, 1, 0, 0, width); break;
  }
  context.drawImage(imgEl, 0, 0);
}

Und Voila, wenn Sie ein Bild für die App freigeben, wird das Bild gedreht und anschließend analysiert, wobei die Ausgabe des gefundenen Texts zurückgegeben wird.

Es hat unglaublich viel Spaß gemacht, dieses kleine Experiment zu erstellen, und es hat mir sofort geholfen. Es wird jedoch das inconsistency of the web platform . Diese APIs sind nicht in allen Browsern verfügbar. Sie sind sogar nicht in allen Chrome-Versionen verfügbar. Das bedeutet, dass ich beim Schreiben dieses Artikels in Chrome OS die App nicht verwenden kann, aber gleichzeitig, wenn ich sie verwenden kann Oh mein Gott, so cool.

Wood Carving found in Engakuji Shrine near Kamakura

Sakura