Github
Live Demo
簡介
Meme Generator 提供用戶製作迷因圖。使用串接 API 的方式將迷因圖呈現在畫面上,輸入文字後便可以下載該迷因圖。
功能
- 提供用戶挑選迷因模板
- 提供用戶輸入文字
- 提供圖片下載
前置作業
- 先看 meme api 所提供的文件
- 申請此網站的賬號(之後在處理 caption 環節時候會用到)
- 把 username 和 password 存在 env file
1 2 3 4 5 6
|
REACT_APP_IMGFLIP_USERNAME = 用戶名 REACT_APP_IMGFLIP_PASSWORD = 密碼
|
步驟
fetch API
https://api.imgflip.com/get_memes
抓取到的資料共有 100 筆,資料如照片所示。
1 2 3 4 5 6 7 8
| const [templates, setTemplates] = useState([]);
useEffect(( fetch("https://api.imgflip.com/get_memes") .then(res => res.json()) .then(json => setTemplates(json.data.memes)) ) ,[])
|
把抓取到的資料呈現到畫面上。
1 2 3 4 5 6 7 8
| return ( <div> {templates.map(template => { return <img style={{width:"200px"}} key={template.id} src={template.url} alt={template.name} /> })} </div> )
|
點擊模板,進入該模板進行編輯
點擊模板的動作可以使用 state 來處理。設置 template state,預設為 null,點擊後更新為該 template. 當 template 為 null 時,顯示所有模板;template 被更新後就顯示被點擊的模板以及供用戶輸入的框框。因為兩個地方都會用到模板,因此把模板拆成獨立的 component。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const [template, setTemplate] = useState(null);
return ( <div> {template && <Meme template = {template} />} {!template && templates.map(template => { return( <Meme template = {template} onClick = {()=> setTemplate(template) } /> ) })} </div> )
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
import React from "react"
function Meme({template, onClick}){ return( <img src={template.url} alt={template.name} onClick={onClick} /> ) }
export default Meme
|
製作讓用戶輸入的框框
使用form製作框框分別為 top text 以及 buttom text. 在 return 中,照片的位置下加入form,form內包含 2 個input。另外加入submit button 做提交資料用。
1 2 3 4 5 6 7 8 9 10
| {template && ( <form onSubmit = {(e) => { e.prevertDefault()}} > <Meme template = {template} /> <input placeholder = "top text"/> <input placeholder = "buttom text"/> <button type = "submit"> Generate </button> </form> )}
|
通過 API 將用戶的資料放到照片上
- 抓取用戶輸入的資料
- 設定點擊 submit button 後會執行的動作
- 使用 imgflip 網站提供的 image caption url 獲取已完成的迷因圖
- 如果成功獲取已經完成的迷因圖就顯示該圖片
抓取用戶輸入的資料
用戶需要輸入的框框有兩個,分別為 top text 和 bottom text,都使用 state 來更新他們的 value.
1 2 3 4 5 6 7 8 9 10
|
const [topText, setTopText] = useState("") const [bottomText, setBottomText] = useState("")
<input placeholder = "top text" value = {topText} onChange={(e) => setTopText(e.target.value)} />
|
點擊 submit 之後,會去 fetch 設定到的 url 格式,這裡使用 async await 來處理。設定 url 的格式:需要 template_id, text0, text1, 用戶名, password.
url 需要的參數通過 objectToQueryParams
傳入. 成功獲取資料後 json 回傳的是一個有已經完成的迷因圖的 url」 和是否成功的提示。將這個 url 存在 meme status 中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
const username = process.env.REACT_APP_IMGFLIP_USERNAME const password = process.env.REACT_APP_IMGFLIP_PASSWORD
const objectToQueryParam = (obj) => { const params = Object.entries(obj).map(([key, value]) => `${key}=${value}`) return "?" + params.join("&") }
<form onSubmit = { async (e) => { e.preventDefault()
const params = { template_id: template.id, text0: topText, text1: bottomText, username: username, password: password, }
const response = await fetch( `https://api.imgflip.com/caption_image${objectToQueryParam(params)}` ) const json = await response.json() setMeme (json.data.url) >
|
顯示已經完成的迷因圖
將這個 url 存在 meme status 中,如果 meme 有資料,則顯示那張已經完成的迷因圖。
1 2 3 4 5 6 7 8 9 10
|
if (meme) { return ( <div> <h2>Feel free to download your meme.</h2> <img src={ meme } alt="custom meme" /> </div> ) }
|
小結
上面的解說都只截取了部分的程式碼,看起來會有一些亂,文末附了完整的程式碼,參照著看會比較清楚。這是一個功能比較單一的迷因產生器,目前只能製作只需要輸入兩段文字的迷因,需要超過兩段文字的迷因圖,文字位置會措置,這個之後再回來修嘿。
完整的程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
|
import React, {useState, useEffect} from "react" import Meme from "./component/meme" import "./index.css"
const username = process.env.REACT_APP_IMGFLIP_USERNAME const password = process.env.REACT_APP_IMGFLIP_PASSWORD
const objectToQueryParam = (obj) => { const params = Object.entries(obj).map(([key, value]) => `${key}=${value}`) return "?" + params.join("&") }
function App() { const [templates, setTemplates] = useState([]) const [template, setTemplate] = useState(null) const [topText, setTopText] = useState("") const [bottomText, setBottomText] = useState("") const [meme, setMeme] = useState(null)
useEffect(() => { fetch("https://api.imgflip.com/get_memes") .then(res => res.json() .then(json => setTemplates(json.data.memes)) ) },[])
if (meme) { return ( <div style={{ textAlign: "center" }}> <h2>Feel free to download your meme.</h2> <img style={{ width: 200 }} src={ meme } alt="custom meme" /> </div> ) }
return ( <div className="all_memes">
{template && ( <form onSubmit = { async (e) => { e.preventDefault()
const params = { template_id: template.id, text0: topText, text1: bottomText, username: username, password: password, }
const response = await fetch( `https://api.imgflip.com/caption_image${objectToQueryParam(params)}` ) const json = await response.json() setMeme (json.data.url) > <Meme template = {template} /> <input placeholder = "top text" value = {topText} onChange={(e) => setTopText(e.target.value)} />
<input placeholder = "bottom text" value = {bottomText} onChange={(e) => setBottomText(e.target.value)} />
<button type = "submit"> Generate </button> </form> )}
{!template && ( <> <h2>Choose a template!</h2> {templates.map((template) => { return ( <> <Meme key = {template.id} template={template} onClick={() => {setTemplate(template) console.log(template)}} /> </> ) })} </> )} </div> ) }
export default App
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
import React from "react"
function Meme({template, onClick}){ return( <img style={{ width:"10rem", height:"10rem", objectFit: "cover", padding: "1.5em"}} src={template.url} alt={template.name} onClick={onClick} /> ) }
export default Meme
|