Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9309

React-Konvaで作成したcanvasをTwitterに画像投稿する

$
0
0
Reactで画像を作成してTwitterAPIを使って画像投稿する。 ReactでCanvasを使う 探したところReact-Kanvaが便利そうだった。使い方は公式や他の方の記事を参照。 公式:https://konvajs.org/docs/react/ 使い方:https://qiita.com/YSRKEN/items/bb8b34510d70ec90eb50 画像をDataURLとして取得 作成したCanvasの画像データを取得する。 useRefのHookを使ってCanvasの要素を取得し、.toDataURL()を呼び出せばOK (https://qiita.com/YSRKEN/items/bb8b34510d70ec90eb50 から引用) import React, { useRef } from "react"; import { Stage as StageType } from 'konva/types/Stage'; const stageRef = useRef<StageType>(null); const process = () => { const temp = stageRef.current; // stageRefの中身(temp )がnullな可能性を考慮してチェック if (temp !== null) { // dataUrlに、画像データがdata URL(MIME Type + base64文字列)形式で書き込まれる。 // toDataURLの引数を変更すれば、PNG以外の画像形式への変換も可能 const dataUrl = temp.toDataURL(); // これ以降、dataUrlを使った操作(画像保存、画像加工等)を行う } }; return <Stage ref={stageRef} width={500} height={500}> <Layer> <Rect stroke='black' strokeWidth={4} x={5} y={5} width={490} height={490} /> <Rect fill='red' x={100} y={100} width={300} height={200} /> </Layer> </Stage>; 余談だが、画像をダウンロードしたい場合は<a>タグにDataURLを入れてダウンロードする。 const downloadCanvas = () => { // dataUrlを取得 const url = stageRef.current.toDataURL(); // <a>タグを作成してdataUrlを入れる const a = document.createElement("a"); a.href = url; a.download = "image.png"; // <a>タグをクリックする処理 a.click(); } ブラウザの幅に合わせてCanvasの拡大率を変える ブラウザのサイズに合わせてCanvasの拡大率を変更したい場合は、<div>要素などでブラウザのフレームサイズを取得して、拡大率を算出する。 // Stageの要素 const stageRef = useRef(null); // 画像を表示するフレームの要素 const frameRef = useRef(null); // Canvasで生成する画像のサイズ const sceneWidth = 960; const sceneHight = 1280; // ブラウザ表示時の拡大率 const [scale, setScale] = useState(1); useEffect(() => { // ブラウザ表示時の拡大率を設定 // ここでは横幅(offsetWidth)に合わせる setScale(frameRef.current.offsetWidth / sceneWidth); }, []); return ( <div ref={frameRef}> <Stage width={sceneWidth * scale} height={sceneHight * scale} scale={{ x: scale, y: scale }} ref={stageRef} > ... </Stage> </div> ) ただ、これを行うと初期表示の一瞬だけブラウザに拡大率1の画像が表示されてしまう。ひとまずCSSのpositionを使って画面外に表示することで対応した。 display:hiddenにしてもいいのだが、画像取得の際にhiddenだと取れなくなってしまう。 {/* 拡大率が1のときは画面外へ飛ばす */} <style jsx> {` .canvas { ${scale == 1 ? " position: absolute; right: -10000px;" : ""} } `} </style> 画像取得時に拡大率を戻す ブラウザのサイズに合わせて拡大率を変更したままDataURLを取得すると、そのサイズの画像が取得されてしまう。 画像取得前に拡大率を1に変更し、取得後にブラウザの拡大率に戻す。 ボタン押下時にツイートフラグ(isTweetCanvas)をtrueにして、useEffectで拡大率(scale)を変更。scaleが1になった次のレンダリングで画像を取得する。 // ブラウザ表示時の拡大率 const [scale, setScale] = useState(1); // ボタン押下のフラグ const [isTweetCanvas, setIsTweetCanvas] = useState(false); useEffect(() => { if (isTweetCanvas) { // 画像を取得する前に画像の拡大率を1にする if (scale != 1) { setScale(1); } else { // 画像取得、ツイート処理(後述) getDataUrlAndTweet(); // 拡大率、ツイートフラグを元に戻す setScale(frameRef.current.offsetWidth / sceneWidth); setisTweetCanvas(false); } } }, [isTweetCanvas, scale]); return (<> <div ref={frameRef}> <Stage width={sceneWidth * scale} height={sceneHight * scale} scale={{ x: scale, y: scale }} ref={stageRef} > ... </Stage> </div> <button onClick={setIsTweetCanvas(true)}>画像をツイート</button> </>) TwitterAPIの呼び出し TwitterAPIのライブラリは見た感じスターが一番多かったTwitを使用。 ※現時点(2021/12/19)でTwitter公式がオススメしているnode-twitter-api-v2は、DataURLを使った画像アップロードはできないので注意。 ブラウザで呼び出すとCORSエラーやfsが開けませんといったエラーになるので、BFFなどnode.jsが動く環境で呼び出す。Next.jsならmiddlewareで良さそう。 DataURLをそのまま入れるとエラーになるので冒頭の「data:image/png;base64」を削除する。 ブラウザ側 browser.tsx async function getDataUrlAndTweet() { // axiosでミドルウェアを呼び出し await axios.post("/api/tweet-canvas", { dataUrl: stageRef.current .toDataURL() // 「data:image/png;base64」を削除 .replace(/^data:image\/png;base64,/, ""), }); } バックエンド側 大体Twitのサンプルをコピー pages/api/tweet-canvas.ts export default async function handler( req: NextApiRequest, res: NextApiResponse ) { // ブラウザ側から取得したデータ const body = req.body; // トークンを設定 // トークンの取得方法は「TwitterAPI トークン取得方法」などでググってください var T = new Twit({ consumer_key: process.env.TWITTER_APP_KEY, consumer_secret: process.env.TWITTER_APP_SECRET, access_token: process.env.TWITTER_TOKEN, access_token_secret: process.env.TWITTER_TOKEN_SECRET, timeout_ms: 60 * 1000, strictSSL: true, }); // 画像をツイッターにアップロード T.post( "media/upload", // ここにDataURLを入れる。 // 「media」ではなく「media_data」を使用する。 { media_data: body.dataUrl }, function (err, data, response) { var mediaIdStr = data.media_id_string; var altText = var meta_params = { media_id: mediaIdStr, alt_text: { text: altText } }; T.post( "media/metadata/create", meta_params, function (err, data, response) { if (!err) { var params = { // ツイート文 status: "loving life #nofilter", media_ids: [mediaIdStr], }; T.post("statuses/update", params, function (err, data, response) { console.log(data); }); } } ); } ); おわり これで画像付きツイートがツイートされているはず。 最初にnode-twitter-api-v2を使ったせいで上手くいかず色々悩んでしまった。 DataURLから「data:image/png;base64」を削除するのも忘れずに。

Viewing all articles
Browse latest Browse all 9309

Trending Articles