【實作記錄】Meme Generator | React

Github
Live Demo

Meme Generator Demo

簡介

Meme Generator 提供用戶製作迷因圖。使用串接 API 的方式將迷因圖呈現在畫面上,輸入文字後便可以下載該迷因圖。

功能

  • 提供用戶挑選迷因模板
  • 提供用戶輸入文字
  • 提供圖片下載

前置作業

  1. 先看 meme api 所提供的文件
  2. 申請此網站的賬號(之後在處理 caption 環節時候會用到)
  3. 把 username 和 password 存在 env file
1
2
3
4
5
6
// filename:.env
// 前綴要加上 REACT_APP 不然無法直接引用
// 不需要額外安裝其他的套件

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
//filename : App.js
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
//filename : App.js
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); 
//設定template的初始值為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
//filename : meme.js 

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
//filename: App.js

const [topText, setTopText] = useState("")
const [bottomText, setBottomText] = useState("")

<input
placeholder = "top text"
value = {topText}
onChange={(e) => setTopText(e.target.value)}
/>


設定點擊 submit button 後會執行的動作

點擊 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
//filename: App.js

//引入存在 env file 的資訊
const username = process.env.REACT_APP_IMGFLIP_USERNAME
const password = process.env.REACT_APP_IMGFLIP_PASSWORD

//設定 url 格式
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
//filename: App.js

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
// filename : App.js

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

//caption url 格式設定
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))
)
},[])

//如果成功獲得 meme url,則顯示該圖片
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 不是空的則顯示被點擊的那張圖
{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
//filename : Meme.js

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

Comments