blur placeholder on article images
This commit is contained in:
1 parent
df229b5e8c
commit
8260e3cf34
9 files changed
+1108
-81
No files matched your search
Generated
+1000
-10
File diff suppressed because it is too large.
Load diff
@@ -28,6 +28,7 @@
|
||||
"react-dom": "^17.0.1",
|
||||
"react-icons": "^4.2.0",
|
||||
"react-use": "^17.2.1",
|
||||
"sharp": "^0.29.1",
|
||||
"sitemap": "^1.5.0",
|
||||
"typograf": "^6.11.1",
|
||||
"use-media": "^1.4.0"
|
||||
@@ -37,6 +38,7 @@
|
||||
"@types/lodash": "^4.14.149",
|
||||
"@types/node": "^12.12.34",
|
||||
"@types/react": "^16.9.32",
|
||||
"@types/sharp": "^0.29.2",
|
||||
"eslint": "^7.1.0",
|
||||
"feed": "^4.1.0",
|
||||
"should": "^8.2.2",
|
||||
|
||||
@@ -112,6 +112,7 @@ const Index: NextPage<Props> = props => {
|
||||
width={x.data.width}
|
||||
height={x.data.height}
|
||||
caption={x.data.caption}
|
||||
blur={x.data.blur}
|
||||
wide={x.wide}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -36,11 +36,14 @@ export const Picture: React.FC<PictureProps> = memo(({ wide = false, ...props })
|
||||
return (
|
||||
<figure className={`${s.pic} ${layout} ${w ? s.wide : ''}`}>
|
||||
<Image
|
||||
className={s.img}
|
||||
src={props.src}
|
||||
alt={props.alt}
|
||||
// alt={props.alt}
|
||||
width={props.width}
|
||||
height={props.height}
|
||||
layout={'responsive'}
|
||||
blurDataURL={props.blur}
|
||||
placeholder={'blur'}
|
||||
/>
|
||||
{!props.caption ? null : (
|
||||
<figcaption>{props.caption}</figcaption>
|
||||
|
||||
@@ -16,8 +16,12 @@
|
||||
max-width: 80%;
|
||||
}
|
||||
|
||||
.pic img {
|
||||
.img {
|
||||
border-radius: var(--radius);
|
||||
|
||||
/* override nextjs blurred placeholder filter */
|
||||
/* need for border radius */
|
||||
filter: none !important;
|
||||
}
|
||||
|
||||
.pic figcaption {
|
||||
|
||||
+8
-4
@@ -20,8 +20,8 @@ export async function requestGet<T>(url: string, defaultResponse: T): Promise<T>
|
||||
/**
|
||||
* This wrapper prevents fetching api request in static export mode and production environment
|
||||
* because nextjs call getInitialProps on the client side
|
||||
*
|
||||
* @param {*} fn
|
||||
*
|
||||
* @param {*} fn
|
||||
*/
|
||||
export function wrapInitialProps(fn: (ctx: NextPageContext) => void) {
|
||||
return async (ctx: NextPageContext) => {
|
||||
@@ -37,9 +37,13 @@ export function createApiUrl(path: string) {
|
||||
return `https://api.tmshv.com/hudozka${path}`
|
||||
}
|
||||
|
||||
export function apiGet<I, O>(factory: (response: I) => O) {
|
||||
export type FactoryFunction<I, O> =
|
||||
| ((response: I) => O)
|
||||
| ((response: I) => Promise<O>)
|
||||
|
||||
export function apiGet<I, O>(factory: FactoryFunction<I, O>) {
|
||||
return async (url: string, defaultResponse: O) => {
|
||||
const res = await requestGet<I>(url, null)
|
||||
const res = await requestGet<I>(url, null as any)
|
||||
if (!res) {
|
||||
return defaultResponse
|
||||
}
|
||||
|
||||
+67
-65
@@ -2,6 +2,7 @@ import { ImageDefinition, IMenu, IPage, ITag, PageCardDto, Token } from "@/types
|
||||
import { asItem } from "./lib";
|
||||
import { StrapiComponentEmbed, StrapiComponent, StrapiHome, StrapiPage, StrapiPageCard, StrapiTag, StrapiMedia, StrapiMenu } from "./types";
|
||||
import { typograf, markdownToHtml } from 'src/lib/text'
|
||||
import { encodeImageToBlurhash } from "./image";
|
||||
|
||||
const md = (text: string) => typograf(markdownToHtml(text))
|
||||
|
||||
@@ -40,70 +41,70 @@ export function createEmbed(component: StrapiComponentEmbed): Token {
|
||||
}
|
||||
}
|
||||
|
||||
export function createPageTokens(components: StrapiComponent[]): Token[] {
|
||||
return components.map(component => {
|
||||
switch (component.__component) {
|
||||
case 'hudozka.text': {
|
||||
return {
|
||||
token: 'html',
|
||||
data: md(component.text),
|
||||
}
|
||||
export async function createPageToken(component: StrapiComponent): Promise<Token> {
|
||||
switch (component.__component) {
|
||||
case 'hudozka.text': {
|
||||
return {
|
||||
token: 'html',
|
||||
data: md(component.text),
|
||||
}
|
||||
|
||||
case 'hudozka.image': {
|
||||
return {
|
||||
token: 'image',
|
||||
wide: component.wide,
|
||||
data: {
|
||||
alt: component.caption,
|
||||
caption: component.caption,
|
||||
src: component.media.url,
|
||||
width: component.media.width,
|
||||
height: component.media.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case 'hudozka.document': {
|
||||
return {
|
||||
token: 'file',
|
||||
data: {
|
||||
url: component.media.url,
|
||||
slug: 'jopa',
|
||||
image_url: component.media.url,
|
||||
file_url: component.media.url,
|
||||
title: component.title,
|
||||
file_size: component.media.size * 1000,
|
||||
// file_size: component.media.size,
|
||||
file_format: component.media.mime,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case 'hudozka.embed': {
|
||||
return createEmbed(component)
|
||||
}
|
||||
|
||||
case 'hudozka.card-grid': {
|
||||
return {
|
||||
token: 'grid',
|
||||
data: {
|
||||
items: component.items
|
||||
.filter(Boolean)
|
||||
.map(createCardGrid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return {
|
||||
token: 'text',
|
||||
data: `
|
||||
${JSON.stringify(component)}
|
||||
`,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
case 'hudozka.image': {
|
||||
const src = component.media.formats?.large?.url ?? component.media.url
|
||||
const thumb = component.media.formats?.thumbnail?.url ?? component.media.url
|
||||
const blur = await encodeImageToBlurhash(thumb)
|
||||
return {
|
||||
token: 'image',
|
||||
wide: component.wide,
|
||||
data: {
|
||||
alt: component.caption,
|
||||
caption: component.caption,
|
||||
src,
|
||||
width: component.media.width,
|
||||
height: component.media.height,
|
||||
blur,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case 'hudozka.document': {
|
||||
return {
|
||||
token: 'file',
|
||||
data: {
|
||||
url: component.media.url,
|
||||
slug: 'jopa',
|
||||
image_url: component.media.url,
|
||||
file_url: component.media.url,
|
||||
title: component.title,
|
||||
file_size: component.media.size * 1000,
|
||||
// file_size: component.media.size,
|
||||
file_format: component.media.mime,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case 'hudozka.embed': {
|
||||
return createEmbed(component)
|
||||
}
|
||||
|
||||
case 'hudozka.card-grid': {
|
||||
return {
|
||||
token: 'grid',
|
||||
data: {
|
||||
items: component.items
|
||||
.filter(Boolean)
|
||||
.map(createCardGrid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return {
|
||||
token: 'text',
|
||||
data: ` ${JSON.stringify(component)}`,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createMenu(res: StrapiMenu): IMenu[] {
|
||||
@@ -118,7 +119,7 @@ export function createMenu(res: StrapiMenu): IMenu[] {
|
||||
}, ...items]
|
||||
}
|
||||
|
||||
export function createPage(res: StrapiPage | StrapiPage[]): IPage | null {
|
||||
export async function createPage(res: StrapiPage | StrapiPage[]): IPage | null {
|
||||
const item = asItem(res)
|
||||
if (!item) {
|
||||
return null
|
||||
@@ -132,6 +133,8 @@ export function createPage(res: StrapiPage | StrapiPage[]): IPage | null {
|
||||
href: `/tags/${tag.slug}`,
|
||||
}))
|
||||
|
||||
const tokens = await Promise.all(item.content.map(createPageToken))
|
||||
|
||||
return {
|
||||
title: item.title,
|
||||
description: item.excerpt,
|
||||
@@ -147,8 +150,7 @@ export function createPage(res: StrapiPage | StrapiPage[]): IPage | null {
|
||||
data: md(`# ${item.title}`),
|
||||
},
|
||||
|
||||
...createPageTokens(item.content)
|
||||
.filter(Boolean),
|
||||
...tokens.filter(Boolean),
|
||||
],
|
||||
tags,
|
||||
// breadcrumb?: [],
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import sharp from "sharp"
|
||||
|
||||
export async function encodeImageToBlurhash(url: string) {
|
||||
const res = await fetch(url)
|
||||
if (!res.ok) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const data = await res.arrayBuffer()
|
||||
const buffer = Buffer.from(data)
|
||||
|
||||
const t = sharp(buffer)
|
||||
.raw()
|
||||
.resize(8, 8, { fit: "inside" })
|
||||
.toFormat('jpeg')
|
||||
|
||||
const { data: out, info } = await t.toBuffer({ resolveWithObject: true })
|
||||
|
||||
return `data:image/jpeg;base64,${out.toString('base64')}`
|
||||
}
|
||||
@@ -104,6 +104,7 @@ export type Pic = {
|
||||
width: number
|
||||
height: number
|
||||
caption?: string
|
||||
blur?: string
|
||||
}
|
||||
|
||||
export type TextToken = {
|
||||
|
||||
Reference in new issue
Block a user