1676901660
Skeleton is an easy way to create sliding CAGradientLayer
animations! It works great for creating skeleton screens:
The entire library comes down to just one public-facing extension:
public extension CAGradientLayer {
public func slide(to dir: Direction, group: ((CAAnimationGroup) -> Void) = { _ in })
public func stopSliding()
}
You can check out the example and the documentation for more.
To run the example project, clone the repo, and run pod install
from the Example directory first.
Skeleton is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod "Skeleton"
Skeleton is also available through Carthage. Add this to your Cartfile:
github "gonzalonunez/Skeleton" ~> 0.4.0
Author: Gonzalonunez
Source Code: https://github.com/gonzalonunez/Skeleton
License: MIT license
1673345400
Windless makes it easy to implement invisible layout loading view.
![]() | ![]() | ![]() |
CocoaPods is a dependency manager for Cocoa projects. You can install it with the following command:
$ gem install cocoapods
CocoaPods 1.1+ is required to build Windless 4.0+.
To integrate Windless into your Xcode project using CocoaPods, specify it in your Podfile
:
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!
target '<Your Target Name>' do
pod 'Windless', '~> 0.1.5'
end
Then, run the following command:
$ pod install
Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
You can install Carthage with Homebrew using the following command:
$ brew update
$ brew install carthage
To integrate Windless into your Xcode project using Carthage, specify it in your Cartfile
:
github "Interactive-Studio/Windless" ~> 0.1.5
Run carthage update
to build the framework and drag the built Windless.framework
into your Xcode project.
If you prefer not to use either of the aforementioned dependency managers, you can integrate Windless into your project manually.
import Windless
class ViewController: UIViewController {
lazy var contentsView = UIView()
var subView1 = UIView()
var subView2 = UIView()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(contentsView)
contentsView.addSubview(subView1)
contentsView.addSubview(subView2)
// start
contentsView.windless
.setupWindlessableViews([subView1, subView2])
.start()
// stop
contentsView.windless.end()
}
}
If you use Storyboard or xib, you only need to set the isWindlessable
flag to true for the views you want to show as fake in the view inspector of the view, and you do not have to pass the view through the setupWindlessableViews
method.
import Windless
class ViewController: UIViewController {
@IBOutlet weak var contentsView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
contentsView.windless.start()
}
}
Depending on the lineHeight
value and the spacing
value, UILabel and UITextView will reconstruct the layout when the windless animation runs.
public protocol CanBeMultipleLines {
var lineHeight: CGFloat { get set }
var spacing: CGFloat { get set }
}
There are several customizable options in Windless.
public class WindlessConfiguration {
/// The direction of windless animation. Defaults to rightDiagonal.
public var direction: WindlessDirection = .rightDiagonal
/// The speed of windless animation. Defaults to 1.
public var speed: Float = 1
/// The duration of the fade used when windless begins. Defaults to 0.
public var beginTime: CFTimeInterval = 0
/// The time interval windless in seconds. Defaults to 4.
public var duration: CFTimeInterval = 4
/// The time interval between windless in seconds. Defaults to 2.
public var pauseDuration: CFTimeInterval = 2
/// gradient animation timingFunction default easeOut
public var timingFuction: CAMediaTimingFunction = .easeOut
/// gradient layer center color default .lightGray
public var animationLayerColor: UIColor = .lightGray
/// Mask layer background color default .groupTableViewBackground
public var animationBackgroundColor: UIColor = .groupTableViewBackground
/// The opacity of the content while it is windless. Defaults to 0.8.
public var animationLayerOpacity: CGFloat = 0.8
}
To set the options, use the apply method as shown below.
import Windless
class ViewController: UIViewController {
@IBOutlet weak var contentsView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
contentsView.windless
.apply {
$0.beginTime = 1
$0.pauseDuration = 2
$0.duration = 3
$0.animationLayerOpacity = 0.5
}
.start()
}
}
If you want to know more detailed usage, please refer to Example.
The isWindlessable
value determines how the loading view looks. The images below show how the loading screen will look according to the isWindlessable
value.
isWindlessable
= 🌀
Author: ParkGwangBeom
Source Code: https://github.com/ParkGwangBeom/Windless
License: MIT license
1655002080
PRIMEREACT UIフレームワークには、すべてのUI要件をスタイリッシュに実装するのに役立つ最高品質の80を超えるReactUIコンポーネントがあります。
Skeletonは、データが読み込まれる前にコンテンツのプレースホルダープレビューを表示して、読み込み時のフラストレーションを軽減します。
前提条件
以下のことをカバーします、
ステップ1
次のコマンドを使用してreactプロジェクトを作成します
npx create-react-app prime-app
cd prime-app
npm start
ステップ2
PrimeReactをインストールするには、以下のコマンドを実行します
npm install primereact primeicons
下の画像に従ってファイルを作成します
ステップ3
App.jsに以下のコードを追加します
import logo from './logo.svg';
import './App.css';
import React, { useState, useEffect } from 'react';
import { AutoComplete } from 'primereact/autocomplete';
import { CountryService } from '../src/CountryService';
import { Skeleton } from 'primereact/skeleton';
import { DataTable } from 'primereact/datatable';
import { Column } from 'primereact/column';
function App() {
const [countries, setCountries] = useState([]);
const [selectedCountry1, setSelectedCountry1] = useState(null);
const [selectedCountry2, setSelectedCountry2] = useState(null);
const [selectedCity, setSelectedCity] = useState(null);
const [selectedCountries, setSelectedCountries] = useState(null);
const [selectedItem, setSelectedItem] = useState(null);
const [filteredCountries, setFilteredCountries] = useState(null);
const [filteredCities, setFilteredCities] = useState(null);
const [filteredItems, setFilteredItems] = useState(null);
const countryservice = new CountryService();
const groupedCities = [
{
label: 'Germany', code: 'DE',
items: [
{ label: 'Berlin', value: 'Berlin' },
{ label: 'Frankfurt', value: 'Frankfurt' },
{ label: 'Hamburg', value: 'Hamburg' },
{ label: 'Munich', value: 'Munich' }
]
},
{
label: 'USA', code: 'US',
items: [
{ label: 'Chicago', value: 'Chicago' },
{ label: 'Los Angeles', value: 'Los Angeles' },
{ label: 'New York', value: 'New York' },
{ label: 'San Francisco', value: 'San Francisco' }
]
},
{
label: 'Japan', code: 'JP',
items: [
{ label: 'Kyoto', value: 'Kyoto' },
{ label: 'Osaka', value: 'Osaka' },
{ label: 'Tokyo', value: 'Tokyo' },
{ label: 'Yokohama', value: 'Yokohama' }
]
}
];
const products = Array.from({ length: 5 });
const bodyTemplate = () => {
return <Skeleton></Skeleton>
}
const items = Array.from({ length: 100000 }).map((_, i) => ({ label: `Item #${i}`, value: i }));
useEffect(() => {
debugger
setCountries(countryservice.getCountries())
}, []); // eslint-disable-line react-hooks/exhaustive-deps
const searchCountry = (event) => {
setTimeout(() => {
let _filteredCountries;
if (!event.query.trim().length) {
_filteredCountries = [...countries];
}
else {
_filteredCountries = countries.filter((country) => {
return country.name.toLowerCase().startsWith(event.query.toLowerCase());
});
}
setFilteredCountries(_filteredCountries);
}, 250);
}
const searchCity = (event) => {
let query = event.query;
let _filteredCities = [];
for (let country of groupedCities) {
let filteredItems = country.items.filter((item) => item.label.toLowerCase().indexOf(query.toLowerCase()) !== -1);
if (filteredItems && filteredItems.length) {
_filteredCities.push({ ...country, ...{ items: filteredItems } });
}
}
setFilteredCities(_filteredCities)
}
const searchItems = (event) => {
//in a real application, make a request to a remote url with the query and return filtered results, for demo we filter at client side
let query = event.query;
let _filteredItems = [];
for (let i = 0; i < items.length; i++) {
let item = items[i];
if (item.label.toLowerCase().indexOf(query.toLowerCase()) === 0) {
_filteredItems.push(item);
}
}
setFilteredItems(_filteredItems);
}
const itemTemplate = (item) => {
return (
<div className="country-item">
<img alt={item.name} src={`images/flag/flag_placeholder.png`} onError={(e) => e.target.src = 'https://www.primefaces.org/wp-content/uploads/2020/05/placeholder.png'} className={`flag flag-${item.code.toLowerCase()}`} />
<div>{item.name}</div>
</div>
);
}
const groupedItemTemplate = (item) => {
return (
<div className="flex align-items-center country-item">
<img alt={item.name} src={`images/flag/flag_placeholder.png`} onError={(e) => e.target.src = 'https://www.primefaces.org/wp-content/uploads/2020/05/placeholder.png'} className={`flag flag-${item.code.toLowerCase()}`} />
<div>{item.label}</div>
</div>
);
}
return (
<div>
<div className="card">
<div className="grid formgrid">
<div className="field col-12 md:col-6">
<h5>Rectangle</h5>
<Skeleton className="mb-2"></Skeleton>
<Skeleton width="10rem" className="mb-2"></Skeleton>
<Skeleton width="5rem" className="mb-2"></Skeleton>
<Skeleton height="2rem" className="mb-2"></Skeleton>
<Skeleton width="10rem" height="4rem"></Skeleton>
</div>
<div className="field col-12 md:col-6">
<h5>Rounded</h5>
<Skeleton className="mb-2" borderRadius="16px"></Skeleton>
<Skeleton width="10rem" className="mb-2" borderRadius="16px"></Skeleton>
<Skeleton width="5rem" borderRadius="16px" className="mb-2"></Skeleton>
<Skeleton height="2rem" className="mb-2" borderRadius="16px"></Skeleton>
<Skeleton width="10rem" height="4rem" borderRadius="16px"></Skeleton>
</div>
<div className="field col-12 md:col-6">
<h5 className="mt-3">Square</h5>
<div className="flex align-items-end">
<Skeleton size="2rem" className="mr-2"></Skeleton>
<Skeleton size="3rem" className="mr-2"></Skeleton>
<Skeleton size="4rem" className="mr-2"></Skeleton>
<Skeleton size="5rem"></Skeleton>
</div>
</div>
<div className="field col-12 md:col-6">
<h5 className="mt-3">Circle</h5>
<div className="flex align-items-end">
<Skeleton shape="circle" size="2rem" className="mr-2"></Skeleton>
<Skeleton shape="circle" size="3rem" className="mr-2"></Skeleton>
<Skeleton shape="circle" size="4rem" className="mr-2"></Skeleton>
<Skeleton shape="circle" size="5rem"></Skeleton>
</div>
</div>
</div>
</div>
<div className="card">
<div className="grid formgrid">
<div className="field col-12 md:col-6 md:pr-6 pr-0">
<h5>Card</h5>
<div className="custom-skeleton p-4">
<div className="flex mb-3">
<Skeleton shape="circle" size="4rem" className="mr-2"></Skeleton>
<div>
<Skeleton width="10rem" className="mb-2"></Skeleton>
<Skeleton width="5rem" className="mb-2"></Skeleton>
<Skeleton height=".5rem"></Skeleton>
</div>
</div>
<Skeleton width="100%" height="150px"></Skeleton>
<div className="flex justify-content-between mt-3">
<Skeleton width="4rem" height="2rem"></Skeleton>
<Skeleton width="4rem" height="2rem"></Skeleton>
</div>
</div>
</div>
<div className="field col-12 md:col-6">
<h5>List</h5>
<div className="custom-skeleton p-4">
<ul className="m-0 p-0">
<li className="mb-3">
<div className="flex">
<Skeleton shape="circle" size="4rem" className="mr-2"></Skeleton>
<div style={{ flex: '1' }}>
<Skeleton width="100%" className="mb-2"></Skeleton>
<Skeleton width="75%"></Skeleton>
</div>
</div>
</li>
<li className="mb-3">
<div className="flex">
<Skeleton shape="circle" size="4rem" className="mr-2"></Skeleton>
<div style={{ flex: '1' }}>
<Skeleton width="100%" className="mb-2"></Skeleton>
<Skeleton width="75%"></Skeleton>
</div>
</div>
</li>
<li className="mb-3">
<div className="flex">
<Skeleton shape="circle" size="4rem" className="mr-2"></Skeleton>
<div style={{ flex: '1' }}>
<Skeleton width="100%" className="mb-2"></Skeleton>
<Skeleton width="75%"></Skeleton>
</div>
</div>
</li>
<li>
<div className="flex">
<Skeleton shape="circle" size="4rem" className="mr-2"></Skeleton>
<div style={{ flex: '1' }}>
<Skeleton width="100%" className="mb-2"></Skeleton>
<Skeleton width="75%"></Skeleton>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
<h5>DataTable</h5>
<DataTable value={products} className="p-datatable-striped">
<Column field="code" header="Code" style={{ width: '25%' }} body={bodyTemplate}></Column>
<Column field="name" header="Name" style={{ width: '25%' }} body={bodyTemplate}></Column>
<Column field="category" header="Category" style={{ width: '25%' }} body={bodyTemplate}></Column>
<Column field="quantity" header="Quantity" style={{ width: '25%' }} body={bodyTemplate}></Column>
</DataTable>
</div>
</div>
);
}
export default App;
ステップ4
以下のコードをindex.htmlに追加します
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="stylesheet" href="https://unpkg.com/primeicons@5.0.0/primeicons.css">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<script src="https://unpkg.com/primereact/core/core.min.js"></script>
<script src="https://unpkg.com/primereact/skeleton/skeleton.min.js"></script>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
ステップ5
App.cssに以下のコードを追加します
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.custom-skeleton {
border: 1px solid var(--surface-d);
border-Radius: 4px;
}
.custom-skeleton ul {
list-style: none;
}
ステップ6
以下のコードをCountryService.jsに追加します
import data from './contires.json';
export class CountryService {
getCountries() {
const result = [];
data.map((datas)=>{
result.push(datas)
});
return result;
}
ステップ7
package.jsonに次のコードを追加します
{
"name": "prime-demo",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^13.5.0",
"primeicons": "^5.0.0",
"primereact": "^8.1.1",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-redux": "^8.0.2",
"react-scripts": "^2.1.3",
"redux": "^4.2.0",
"web-vitals": "^2.1.4",
"primeflex": "^3.1.0",
"react-transition-group": "^4.4.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
ステップ8
以下のコマンドを実行します
npm i
npm start
ステップ9
この記事では、reactプロジェクトを作成する方法、PrimeReact UIを設定する方法、PrimeReactUIのSkeletonUIウィジェットを追加する方法を学びました。
このストーリーは、もともとhttps://www.c-sharpcorner.com/article/how-to-create-skeleton-in-react-js-using-primereactprimefaces-ui/で公開されました
1655001720
El marco de interfaz de usuario PRIMEREACT tiene más de 80 componentes de interfaz de usuario React con una calidad de primer nivel para ayudarlo a implementar todos sus requisitos de interfaz de usuario con estilo.
Skeleton es mostrar una vista previa de marcador de posición de su contenido antes de que se carguen los datos para reducir la frustración del tiempo de carga.
condiciones previas
Cubrimos las siguientes cosas,
Paso 1
Cree un proyecto de reacción usando el siguiente comando
npx create-react-app prime-app
cd prime-app
npm start
Paso 2
Ejecute el siguiente comando para instalar PrimeReact
npm install primereact primeicons
Cree los archivos de acuerdo con la imagen de abajo.
Paso 3
Agregue el siguiente código en App.js
import logo from './logo.svg';
import './App.css';
import React, { useState, useEffect } from 'react';
import { AutoComplete } from 'primereact/autocomplete';
import { CountryService } from '../src/CountryService';
import { Skeleton } from 'primereact/skeleton';
import { DataTable } from 'primereact/datatable';
import { Column } from 'primereact/column';
function App() {
const [countries, setCountries] = useState([]);
const [selectedCountry1, setSelectedCountry1] = useState(null);
const [selectedCountry2, setSelectedCountry2] = useState(null);
const [selectedCity, setSelectedCity] = useState(null);
const [selectedCountries, setSelectedCountries] = useState(null);
const [selectedItem, setSelectedItem] = useState(null);
const [filteredCountries, setFilteredCountries] = useState(null);
const [filteredCities, setFilteredCities] = useState(null);
const [filteredItems, setFilteredItems] = useState(null);
const countryservice = new CountryService();
const groupedCities = [
{
label: 'Germany', code: 'DE',
items: [
{ label: 'Berlin', value: 'Berlin' },
{ label: 'Frankfurt', value: 'Frankfurt' },
{ label: 'Hamburg', value: 'Hamburg' },
{ label: 'Munich', value: 'Munich' }
]
},
{
label: 'USA', code: 'US',
items: [
{ label: 'Chicago', value: 'Chicago' },
{ label: 'Los Angeles', value: 'Los Angeles' },
{ label: 'New York', value: 'New York' },
{ label: 'San Francisco', value: 'San Francisco' }
]
},
{
label: 'Japan', code: 'JP',
items: [
{ label: 'Kyoto', value: 'Kyoto' },
{ label: 'Osaka', value: 'Osaka' },
{ label: 'Tokyo', value: 'Tokyo' },
{ label: 'Yokohama', value: 'Yokohama' }
]
}
];
const products = Array.from({ length: 5 });
const bodyTemplate = () => {
return <Skeleton></Skeleton>
}
const items = Array.from({ length: 100000 }).map((_, i) => ({ label: `Item #${i}`, value: i }));
useEffect(() => {
debugger
setCountries(countryservice.getCountries())
}, []); // eslint-disable-line react-hooks/exhaustive-deps
const searchCountry = (event) => {
setTimeout(() => {
let _filteredCountries;
if (!event.query.trim().length) {
_filteredCountries = [...countries];
}
else {
_filteredCountries = countries.filter((country) => {
return country.name.toLowerCase().startsWith(event.query.toLowerCase());
});
}
setFilteredCountries(_filteredCountries);
}, 250);
}
const searchCity = (event) => {
let query = event.query;
let _filteredCities = [];
for (let country of groupedCities) {
let filteredItems = country.items.filter((item) => item.label.toLowerCase().indexOf(query.toLowerCase()) !== -1);
if (filteredItems && filteredItems.length) {
_filteredCities.push({ ...country, ...{ items: filteredItems } });
}
}
setFilteredCities(_filteredCities)
}
const searchItems = (event) => {
//in a real application, make a request to a remote url with the query and return filtered results, for demo we filter at client side
let query = event.query;
let _filteredItems = [];
for (let i = 0; i < items.length; i++) {
let item = items[i];
if (item.label.toLowerCase().indexOf(query.toLowerCase()) === 0) {
_filteredItems.push(item);
}
}
setFilteredItems(_filteredItems);
}
const itemTemplate = (item) => {
return (
<div className="country-item">
<img alt={item.name} src={`images/flag/flag_placeholder.png`} onError={(e) => e.target.src = 'https://www.primefaces.org/wp-content/uploads/2020/05/placeholder.png'} className={`flag flag-${item.code.toLowerCase()}`} />
<div>{item.name}</div>
</div>
);
}
const groupedItemTemplate = (item) => {
return (
<div className="flex align-items-center country-item">
<img alt={item.name} src={`images/flag/flag_placeholder.png`} onError={(e) => e.target.src = 'https://www.primefaces.org/wp-content/uploads/2020/05/placeholder.png'} className={`flag flag-${item.code.toLowerCase()}`} />
<div>{item.label}</div>
</div>
);
}
return (
<div>
<div className="card">
<div className="grid formgrid">
<div className="field col-12 md:col-6">
<h5>Rectangle</h5>
<Skeleton className="mb-2"></Skeleton>
<Skeleton width="10rem" className="mb-2"></Skeleton>
<Skeleton width="5rem" className="mb-2"></Skeleton>
<Skeleton height="2rem" className="mb-2"></Skeleton>
<Skeleton width="10rem" height="4rem"></Skeleton>
</div>
<div className="field col-12 md:col-6">
<h5>Rounded</h5>
<Skeleton className="mb-2" borderRadius="16px"></Skeleton>
<Skeleton width="10rem" className="mb-2" borderRadius="16px"></Skeleton>
<Skeleton width="5rem" borderRadius="16px" className="mb-2"></Skeleton>
<Skeleton height="2rem" className="mb-2" borderRadius="16px"></Skeleton>
<Skeleton width="10rem" height="4rem" borderRadius="16px"></Skeleton>
</div>
<div className="field col-12 md:col-6">
<h5 className="mt-3">Square</h5>
<div className="flex align-items-end">
<Skeleton size="2rem" className="mr-2"></Skeleton>
<Skeleton size="3rem" className="mr-2"></Skeleton>
<Skeleton size="4rem" className="mr-2"></Skeleton>
<Skeleton size="5rem"></Skeleton>
</div>
</div>
<div className="field col-12 md:col-6">
<h5 className="mt-3">Circle</h5>
<div className="flex align-items-end">
<Skeleton shape="circle" size="2rem" className="mr-2"></Skeleton>
<Skeleton shape="circle" size="3rem" className="mr-2"></Skeleton>
<Skeleton shape="circle" size="4rem" className="mr-2"></Skeleton>
<Skeleton shape="circle" size="5rem"></Skeleton>
</div>
</div>
</div>
</div>
<div className="card">
<div className="grid formgrid">
<div className="field col-12 md:col-6 md:pr-6 pr-0">
<h5>Card</h5>
<div className="custom-skeleton p-4">
<div className="flex mb-3">
<Skeleton shape="circle" size="4rem" className="mr-2"></Skeleton>
<div>
<Skeleton width="10rem" className="mb-2"></Skeleton>
<Skeleton width="5rem" className="mb-2"></Skeleton>
<Skeleton height=".5rem"></Skeleton>
</div>
</div>
<Skeleton width="100%" height="150px"></Skeleton>
<div className="flex justify-content-between mt-3">
<Skeleton width="4rem" height="2rem"></Skeleton>
<Skeleton width="4rem" height="2rem"></Skeleton>
</div>
</div>
</div>
<div className="field col-12 md:col-6">
<h5>List</h5>
<div className="custom-skeleton p-4">
<ul className="m-0 p-0">
<li className="mb-3">
<div className="flex">
<Skeleton shape="circle" size="4rem" className="mr-2"></Skeleton>
<div style={{ flex: '1' }}>
<Skeleton width="100%" className="mb-2"></Skeleton>
<Skeleton width="75%"></Skeleton>
</div>
</div>
</li>
<li className="mb-3">
<div className="flex">
<Skeleton shape="circle" size="4rem" className="mr-2"></Skeleton>
<div style={{ flex: '1' }}>
<Skeleton width="100%" className="mb-2"></Skeleton>
<Skeleton width="75%"></Skeleton>
</div>
</div>
</li>
<li className="mb-3">
<div className="flex">
<Skeleton shape="circle" size="4rem" className="mr-2"></Skeleton>
<div style={{ flex: '1' }}>
<Skeleton width="100%" className="mb-2"></Skeleton>
<Skeleton width="75%"></Skeleton>
</div>
</div>
</li>
<li>
<div className="flex">
<Skeleton shape="circle" size="4rem" className="mr-2"></Skeleton>
<div style={{ flex: '1' }}>
<Skeleton width="100%" className="mb-2"></Skeleton>
<Skeleton width="75%"></Skeleton>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
<h5>DataTable</h5>
<DataTable value={products} className="p-datatable-striped">
<Column field="code" header="Code" style={{ width: '25%' }} body={bodyTemplate}></Column>
<Column field="name" header="Name" style={{ width: '25%' }} body={bodyTemplate}></Column>
<Column field="category" header="Category" style={{ width: '25%' }} body={bodyTemplate}></Column>
<Column field="quantity" header="Quantity" style={{ width: '25%' }} body={bodyTemplate}></Column>
</DataTable>
</div>
</div>
);
}
export default App;
Paso 4
Agregue el siguiente código en index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="stylesheet" href="https://unpkg.com/primeicons@5.0.0/primeicons.css">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<script src="https://unpkg.com/primereact/core/core.min.js"></script>
<script src="https://unpkg.com/primereact/skeleton/skeleton.min.js"></script>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
Paso 5
Agregue el siguiente código en App.css
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.custom-skeleton {
border: 1px solid var(--surface-d);
border-Radius: 4px;
}
.custom-skeleton ul {
list-style: none;
}
Paso 6
Agregue el siguiente código en CountryService.js
import data from './contires.json';
export class CountryService {
getCountries() {
const result = [];
data.map((datas)=>{
result.push(datas)
});
return result;
}
Paso 7
Agregue el siguiente código en package.json
{
"name": "prime-demo",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^13.5.0",
"primeicons": "^5.0.0",
"primereact": "^8.1.1",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-redux": "^8.0.2",
"react-scripts": "^2.1.3",
"redux": "^4.2.0",
"web-vitals": "^2.1.4",
"primeflex": "^3.1.0",
"react-transition-group": "^4.4.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
Paso 8
Ejecute los siguientes comandos
npm i
npm start
Paso 9
En este artículo, aprendimos cómo crear un proyecto de reacción, configurar la interfaz de usuario de PrimeReact y agregar widgets de interfaz de usuario de esqueleto de la interfaz de usuario de PrimeReact.
Esta historia se publicó originalmente en https://www.c-sharpcorner.com/article/how-to-create-skeleton-in-react-js-using-primereactprimefaces-ui/
1653115680
Laminas API Tools Skeleton Application
Please see the composer.json file.
Grab the latest release via the Laminas API Tools website and/or the releases page; each release has distribution tarballs and zipballs available.
Untar it:
$ tar xzf api-tools-skeleton-{version}.tgz
(Where {version}
is the version you downloaded.)
Or unzip, if you chose the zipball:
$ unzip api-tools-skeleton-{version}.zip
(Where {version}
is the version you downloaded.)
You can use the create-project
command from Composer to create the project in one go (you need to install composer):
$ curl -s https://getcomposer.org/installer | php -- --filename=composer
$ composer create-project -sdev laminas-api-tools/api-tools-skeleton path/to/install
First, clone the repository:
# git clone https://github.com/laminas-api-tools/api-tools-skeleton.git # optionally, specify the directory in which to clone
$ cd path/to/install
At this point, you need to use Composer to install dependencies. Assuming you already have Composer:
$ composer install
Once you have the basic installation, you need to put it in development mode:
$ cd path/to/install
$ composer development-enable
Now, fire it up! Do one of the following:
public/
directory of the projectIn the latter case, do the following:
$ cd path/to/install
$ php -S 0.0.0.0:8080 -ddisplay_errors=0 -t public public/index.php
# OR use the composer alias:
$ composer serve
You can then visit the site at http://localhost:8080/ - which will bring up a welcome page and the ability to visit the dashboard in order to create and inspect your APIs.
Apache forbids the character sequences %2F
and %5C
in URI paths. However, the Laminas API Tools Admin API uses these characters for a number of service endpoints. As such, if you wish to use the Admin UI and/or Admin API with Apache, you will need to configure your Apache vhost/project to allow encoded slashes:
AllowEncodedSlashes On
This change will need to be made in your server's vhost file (it cannot be added to .htaccess
).
Disable all opcode caches when running the admin!
The admin cannot and will not run correctly when an opcode cache, such as APC or OpCache, is enabled. Laminas API Tools does not use a database to store configuration; instead, it uses PHP configuration files. Opcode caches will cache these files on first load, leading to inconsistencies as you write to them, and will typically lead to a state where the admin API and code become unusable.
The admin is a development tool, and intended for use a development environment. As such, you should likely disable opcode caching, regardless.
When you are ready to deploy your API to production, however, you can disable development mode, thus disabling the admin interface, and safely run an opcode cache again. Doing so is recommended for production due to the tremendous performance benefits opcode caches provide.
The display_errors
php.ini
setting is useful in development to understand what warnings, notices, and error conditions are affecting your application. However, they cause problems for APIs: APIs are typically a specific serialization format, and error reporting is usually in either plain text, or, with extensions like XDebug, in HTML. This breaks the response payload, making it unusable by clients.
For this reason, we recommend disabling display_errors
when using the Laminas API Tools admin interface. This can be done using the -ddisplay_errors=0
flag when using the built-in PHP web server, or you can set it in your virtual host or server definition. If you disable it, make sure you have reasonable error log settings in place. For the built-in PHP web server, errors will be reported in the console itself; otherwise, ensure you have an error log file specified in your configuration.
display_errors
should never be enabled in production, regardless.
If you prefer to develop with Vagrant, there is a basic vagrant recipe included with this project.
This recipe assumes that you already have Vagrant installed. The virtual machine will try to use localhost:8080 by default, so if you already have a server on this port of your host machine, you need to shut down the conflicting server first, or if you know how, you can reconfigure the ports in Vagrantfile.
Assuming you have Vagrant installed and assuming you have no port conflicts, you can bring up the Vagrant machine with the standard up
command:
$ vagrant up
When the machine comes up, you can ssh to it with the standard ssh forward agent:
$ vagrant ssh
The web root is inside the shared directory, which is at /var/www
; this is also the home directory for the vagrant issue, which will be the initial directory you land in once you connect via SSH.
The image installs composer during provisioning, meaning you can use it to install and update dependencies:
# Install dependencies:
$ vagrant ssh -c 'composer install'
# Update dependencies:
$ vagrant ssh -c 'composer update'
You can also manipulate development mode:
$ vagrant ssh -c 'composer development-enable'
$ vagrant ssh -c 'composer development-disable'
$ vagrant ssh -c 'composer development-status'
Vagrant and VirtualBox
The vagrant image is based on
bento/ubuntu-16.04
. If you are using VirtualBox as a provider, you will need:
- Vagrant 1.8.5 or later
- VirtualBox 5.0.26 or later
For vagrant documentation, please refer to vagrantup.com
If you develop or deploy using Docker, we provide configuration for you.
Prepare your development environment using docker compose:
$ git clone https://github.com/laminas-api-tools/api-tools-skeleton
$ cd api-tools-skeleton
$ docker-compose build
# Install dependencies via composer, if you haven't already:
$ docker-compose run api-tools composer install
# Enable development mode:
$ docker-compose run api-tools composer development-enable
Start the container:
$ docker-compose up
Access Laminas API Tools from http://localhost:8080/
or http://<boot2docker ip>:8080/
if on Windows or Mac.
You may also use the provided Dockerfile
directly if desired.
Once installed, you can use the container to update dependencies:
$ docker-compose run api-tools composer update
Or to manipulate development mode:
$ docker-compose run api-tools composer development-enable
$ docker-compose run api-tools composer development-disable
$ docker-compose run api-tools composer development-status
The skeleton ships with minimal QA tooling by default, including laminas/laminas-test. We supply basic tests for the shipped Application\Controller\IndexController
.
We also ship with configuration for phpcs. If you wish to add this QA tool, execute the following:
$ composer require --dev squizlabs/php_codesniffer
We provide aliases for each of these tools in the Composer configuration:
# Run CS checks:
$ composer cs-check
# Fix CS errors:
$ composer cs-fix
# Run PHPUnit tests:
$ composer test
Author: laminas-api-tools
Source Code: https://github.com/laminas-api-tools/api-tools-skeleton
License: BSD-3-Clause License
1651014000
コンテンツローダー、スケルトンスクリーン、ゴースト要素、およびコンテンツプレースホルダー。これらは、今日調査するエフェクトに付けられた名前です。
Linkedin、Facebook、Youtube、Slackなどの多くの企業は、お気づきかもしれませんが、アプリやWebサイトでこの効果を使用しています。
開発者はウェブサイトをできるだけ早く読み込むことを望んでいますが、ページに大量のデータをレンダリングする必要がある場合があるため、スケルトン画面は優れたオプションです。
この記事はあなたが持っていることを前提としています:
このプロジェクトではHTMLとSASSを使用します。SASSの使用を開始したい場合は、この初心者向けガイドを確認してください。
スケルトン画面は、データの読み込み中にWebサイトのレイアウトをシミュレートするアニメーションのプレースホルダーです。
一部のコンテンツが読み込まれていることをユーザーに通知し、さらに重要なことに、画像、テキスト、カードなど、何が読み込まれているかを示します。
これにより、ユーザーは、表示される前にどのタイプのコンテンツがロードされているかをすでに知っているため、Webサイトが高速であるという印象を与えることができます。これは、知覚パフォーマンスと呼ばれます。
FacebookとLinkedInのスケルトン画面の例を次に示します。
LinkedInのホームフィードの読み込み状態
Facebookのホームフィードの読み込み状態
スケルトンスクリーンには主に2つのタイプがあります。
コンテンツプレースホルダーは通常、FacebookとLinkedInの上の画像に示されているように、ページの外観をシミュレートする明るい灰色のボックスと円です。
カラープレースホルダーは、UIレイアウトだけでなく、ドミナントカラーもシミュレートするため、作成がより困難です。最も一般的には、PinterestやUnsplashなどの画像に焦点を当てたWebサイトで見られます。
Pinterestのカラープレースホルダーの例
スケルトン画面を実装する際には、ウェブサイトやアプリで達成しようとしている目標を念頭に置き、コンテンツの読み込みを優先する必要があります。
スケルトンのロード画面を使用することは、実際のパフォーマンスの最適化をスキップする言い訳にはなりません。意味のあるコンテンツをキャッシュして表示できるのであれば、それは良いことです。
このセクションでは、理解しやすいように、段階的なプロセスに従ってスケルトンのロード画面の実装について詳しく説明します。
daily.devのフィードセクションのようなものを作成します。
まず、私と一緒にコーディングするには、ここでレイアウトのスターターコードを複製またはダウンロードします。DownGitを使用してファイルをダウンロードできます。
コードにはカードレイアウトが含まれているため、ここから次の手順に進みます。
まず、IDEで開発サーバーを起動し、ブラウザを開きます。
スターターカードのレイアウト
スケルトンの読み込み用に作成する要素は、ロゴ画像、タイトル、詳細、カバー画像、フッターセクションの5つです。
デイリー開発者のスケルトン要素
skeleton
次に、上記の要素の場所にクラスを追加します。
ロゴは、
<img class="card__header header__img skeleton" />
タイトルには、上の写真にある2本の線を表す2つのdivがあります。
<div class="skeleton skeleton-text"></div>
<div class="skeleton skeleton-text"></div>
詳細については、クラスのdiv内に次のコードを追加してくださいbody__text
。
<div class="skeleton skeleton-text skeleton-text__body"></div>
div内body__img
に、次のコードを追加します。
<img class="skeleton" alt="" id="cover-img" />
フッターには、次のコードを追加します。
<div class="skeleton skeleton-text skeleton-footer"></div>
これで、カードの完全なHTMLコードは次のようになります。
<a class="card" id="card-link" target="_blank">
<div class="card__header">
<div>
<img class="card__header header__img skeleton" id="logo-img" alt="" />
</div>
<h3 class="card__header header__title" id="card-title">
<div class="skeleton skeleton-text"></div>
<div class="skeleton skeleton-text"></div>
</h3>
</div>
<div class="card__body">
<div class="card__body body__text" id="card-details">
<div class="skeleton skeleton-text skeleton-text__body"></div>
</div>
<div class="card__body body__img">
<img class="skeleton" alt="" id="cover-img" />
</div>
</div>
<div class="card__footer" id="card-footer">
<div class="skeleton skeleton-text skeleton-footer"></div>
</div>
</a>
次に、スケルトンコンポーネントを作成するためのスタイルを追加しましょう。
.skeleton {
animation: skeleton-loading 1s linear infinite alternate;
}
@keyframes skeleton-loading {
0% {
background-color: hsl(200, 20%, 80%);
}
100% {
background-color: hsl(200, 20%, 95%);
}
}
.skeleton-text {
width: 100%;
height: 0.7rem;
margin-bottom: 0.5rem;
border-radius: 0.25rem;
}
.skeleton-text__body {
width: 75%;
}
.skeleton-footer {
width: 30%;
}
結果のレイアウトは次のとおりです。
カードコンポーネントの読み込み
とファイルの要素template
の間にタグを挿入します。containercardindex.html
上の画像template
には、コメントアウトしたタグがあります。そうです、その有効なHTML要素です;)。これは、スクリプトによって複製およびドキュメントに挿入できるHTMLのフラグメントを宣言するために使用されます。
<template id="card-template">
そのため、 div</template>
の終了タグの後に必ず終了タグを追加してください。card
次に、カードテンプレートのクローンを作成するために使用するJavasScriptコードを見てみましょう。
script
タグの終わりの直前にタグを作成しbody
、次のコードを追加します。
const container = document.querySelector(".container");
const cardTemplate = document.getElementById("card-template");
for (let i = 0; i < 10; i++) {
container.append(cardTemplate.content.cloneNode(true));
}
上記のコードは、ページコンテナとカードテンプレートを取得し、カードの9つのクローン/コピーを作成します(合計で10を作成します)。次に、カードをコンテナに追加/挿入します。
クローンカードスケルトンUI
ページにコンテンツを追加する前に、いくつかのデータが必要です。通常は外部のWebサイトでデータを取得する必要がありますが、このプロジェクト用に特別に設定したWebサイトを使用します。
まずdata.json
、プロジェクトフォルダにというファイルを作成します。
次のコードをJSONファイルに追加します。
[
{
"id": 1,
"logoImage": "https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/4a287b2e7cb5499bae863f8e7137cdb4",
"title": "Writing Cleaner CSS Using BEM ",
"details": "Mar 12, 2022 · 4m read time",
"coverImage": "https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/dd19e7a56475f39ab1c38167c02c7b58",
"link": "https://israelmitolu.hashnode.dev/writing-cleaner-css-using-bem-methodology"
},
{
"id": 2,
"logoImage": "https://daily-now-res.cloudinary.com/image/upload/t_logo,f_auto/v1628412854/logos/freecodecamp",
"title": "The Beginner's Guide to Sass",
"details": "Apr 05, 2022 · 8m read time",
"coverImage": "https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/bec6719be210973098293a32dc732d1e",
"link": "https://www.freecodecamp.org/news/the-beginners-guide-to-sass/"
},
{
"id": 3,
"logoImage": "https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/devto",
"title": "I made Squid Game with Javascript",
"details": "Oct 25, 2021 · 3m read time",
"coverImage": "https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/1f947033365381cbe322ddf294ad7169",
"link": "https://dev.to/0shuvo0/i-made-squid-game-with-javascript-10j9"
},
{
"id": 4,
"logoImage": "https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/4a287b2e7cb5499bae863f8e7137cdb4",
"title": "Using Custom Cursors with Javascript for a Better User Experience",
"details": "Feb 12, 2022 · 9m read time",
"coverImage": "https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/3d056b99c95b37cd35ae5cfc6a8b38be",
"link": "https://israelmitolu.hashnode.dev/using-custom-cursors-with-javascript-for-a-better-user-experience"
},
{
"id": 5,
"logoImage": "https://daily-now-res.cloudinary.com/image/upload/t_logo,f_auto/v1628412854/logos/freecodecamp",
"title": "React Best Practices - Tips for Writing Better React Code in 2022",
"details": "Feb 03, 2022 · 31m read time",
"coverImage": "https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/5a629fff5583f9ab5f0931d14736b299",
"link": "https://www.freecodecamp.org/news/best-practices-for-react/"
},
{
"id": 6,
"logoImage": "https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/tnw",
"title": "You suck at Googling: 5 tips to improve your search skills",
"details": "Mar 31, 2022 · 4m read time",
"coverImage": "https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/e318150ae67c2083ff3585a96f366f7b",
"link": "https://thenextweb.com/news/5-tips-to-improve-your-google-search-skills"
},
{
"id": 7,
"logoImage": "https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/logrocket",
"title": "A better way of solving prop drilling in React apps",
"details": "Jan 14, 2022 · 13m read time",
"coverImage": "https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/6fe4c4060bca638b419d8b2c63d8eaf7",
"link": "https://blog.logrocket.com/solving-prop-drilling-react-apps/"
},
{
"id": 8,
"logoImage": "https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/dz",
"title": "Golang and Event-Driven Architecture",
"details": "Apr 18, 2022 · 6m read time",
"coverImage": "https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/d06eddd82c62288df6e2600bcda61579",
"link": "https://dzone.com/articles/golang-and-event-driven-architecture"
},
{
"id": 9,
"logoImage": "https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/4a287b2e7cb5499bae863f8e7137cdb4",
"title": "Introduction to Git In 16 Minutes",
"details": "Mar 18, 2021 · 8m read time",
"coverImage": "https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/3c02111a8f242f607551500432e17a78",
"link": "https://vickyikechukwu.hashnode.dev/introduction-to-git-in-16-minutes"
},
{
"id": 10,
"logoImage": "https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/4a287b2e7cb5499bae863f8e7137cdb4",
"title": "How to Create a Sleek Preloader Animation Using GSAP Timeline",
"details": "Jan 25, 2022 · 7m read time",
"coverImage": "https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/e238c35cb9d41dd9a5475602aef00119",
"link": "https://israelmitolu.hashnode.dev/how-to-create-a-sleek-preloader-animation-using-gsap-timeline"
}
]
Daily Devのフィードセクションを複製するために、ID、ロゴ画像、タイトル、詳細、カバー画像などのプロパティを持つオブジェクトの配列を持つデータを作成しました。
JavaScriptを格納するスクリプトタグに次のコードを追加します。
fetch("data.json")
.then((response) => response.json())
.then((posts) => {
container.innerHTML = "";
posts.forEach((post) => {
const div = cardTemplate.content.cloneNode(true);
div.getElementById("card-link").href = post.link;
div.getElementById("logo-img").src = post.logoImage;
div.getElementById("card-title").textContent = post.title;
div.getElementById("card-details").textContent = post.details;
div.getElementById("cover-img").src = post.coverImage;
div.getElementById(
"card-footer"
).innerHTML = ` <ion-icon name="arrow-up"></ion-icon>
<ion-icon name="chatbox-ellipses"></ion-icon>
<ion-icon name="bookmark"></ion-icon>`;
container.append(div);
});
});
上記のコードは、カードの読み込みが完了したら、カードにコンテンツを追加するために使用するものです。
それでは、コードを少しずつ説明しましょう。
fetch("data.json")
.then((response) => response.json())
ここに、リソースへのパスを設定する基本的なフェッチ要求があります。この場合、data.json
ファイル。外部APIの場合は、エンドポイントURLを引数として使用します。
このfetch()
メソッドは、JSON応答本文を直接返すのではなく、Responseオブジェクトで解決されるpromiseを返します。
詳細については、 MDNドキュメントをご覧ください。
.then((posts) => {
container.innerHTML = "";
posts.forEach((post) => {
const div = cardTemplate.content.cloneNode(true);
div.getElementById("logo-img").src = post.logoImage;
div.getElementById("card-title").textContent = post.title;
div.getElementById("card-details").textContent = post.details;
div.getElementById("cover-img").src = post.coverImage;
div.getElementById(
"card-footer"
).innerHTML = `<ion-icon name="arrow-up"></ion-icon>
<ion-icon name="chatbox-ellipses"></ion-icon>
<ion-icon name="bookmark"></ion-icon>`;
container.append(div);
});
});
ここでは、データをフェッチした後に何が起こるかを定義します。
コードは最初にページをクリアし、次にforEach()
JSONファイルからプロパティを抽出するメソッドを実行し、プロパティを使用してカード要素(ロゴ画像、カードタイトルなど)に入力し.textContent
ます。
最後に、フッターでは.innerHTML
、アイコンをHTMLコンテンツとして挿入していました。
すべてを正しく追加した場合、エラーは発生しないはずです。これは、完全に機能するスケルトン読み込みUIです。
完成したDailyDevスケルトンUI
Githubのライブデモとソースコードリポジトリを確認してください。
このスケルトン画面はユーザーのネットワーク速度に依存するため、タイムアウトを設定しなかったことに注意することが重要です。
さまざまなネットワーク速度でシミュレートする場合は、ブラウザのDevtoolsの[ネットワーク]タブに移動します。
Chromev100でそれを行う方法は次のとおりです。
ChromeDevToolsのスロットルネットワーク
デフォルトのオプションが適切でない場合は、ドロップダウンメニューの最上部にあるオプションを選択して、カスタムのネットワークスロットリングプロファイルを作成できます。
あなたは最後までそれを成し遂げました!スケルトンの読み込みと、データを読み込むときに速度の錯覚を作成することでユーザーエクスペリエンスにどのように貢献するかを学び、独自の実装を行いました。
このチュートリアルがお役に立てば幸いです。また、さまざまなスケルトンロード画面を作成するための良い出発点として役立つことを願っています。
この記事が洞察に満ちていると感じた場合は、友達やネットワークと共有してください。また、 Twitterやブログで私と連絡を取り、より良い開発者になるためのリソースや記事を共有してください。
読んでくれてありがとう、そして幸せなコーディング!
1651014000
Cargadores de contenido, pantallas esqueléticas, elementos fantasma y marcadores de posición de contenido. Estos son los nombres dados al efecto que exploraremos hoy.
Muchas empresas, como Linkedin, Facebook, Youtube y Slack, utilizan este efecto en sus aplicaciones y sitios web, como habrás notado.
Por mucho que los desarrolladores queramos que nuestros sitios web se carguen lo más rápido posible, hay momentos en los que es necesario representar una gran cantidad de datos en la página, por lo que las pantallas Skeleton son una excelente opción.
Este artículo asume que usted tiene:
Usaremos HTML y SASS para este proyecto. Si desea comenzar con SASS, consulte esta Guía para principiantes.
Una pantalla esqueleto es un marcador de posición animado que simula el diseño de un sitio web mientras se cargan los datos.
Le informan al usuario que se está cargando algún contenido y, lo que es más importante, brindan una indicación de lo que se está cargando, ya sea una imagen, texto, tarjeta, etc.
Esto le da al usuario la impresión de que el sitio web es más rápido porque ya sabe qué tipo de contenido se está cargando antes de que aparezca. Esto se conoce como rendimiento percibido .
Estos son algunos ejemplos de pantallas esqueléticas de Facebook y LinkedIn:
Estado de carga del feed de inicio de LinkedIn
Estado de carga del feed de inicio de Facebook
Hay 2 tipos principales de pantallas de esqueleto:
Los marcadores de posición de contenido suelen ser cuadros y círculos de color gris claro que simulan la apariencia de la página, como se muestra en las imágenes de arriba para Facebook y LinkedIn.
Los marcadores de posición de color son más difíciles de crear porque simulan no solo el diseño de la interfaz de usuario, sino también el color dominante. Se encuentra más comúnmente en sitios web enfocados en imágenes como Pinterest y Unsplash.
Ejemplo de marcador de posición de color de Pinterest
Al implementar pantallas de esqueleto, debemos tener en cuenta el objetivo que intentamos lograr con el sitio web o la aplicación, y priorizar la carga del contenido.
El uso de pantallas de carga esqueléticas no es excusa para omitir la optimización del rendimiento real, y si puede almacenar en caché contenido significativo y mostrarlo, será bueno.
En esta sección, profundizaremos en la implementación de la pantalla de carga básica siguiendo un proceso paso a paso para que sea más fácil de entender.
Construiremos uno como la sección de noticias de daily.dev.
Primero, para codificar conmigo, clone o descargue el código de inicio para el diseño aquí . Puede descargar los archivos utilizando DownGit .
El código contiene el diseño de la tarjeta, por lo que continuaremos desde aquí en los próximos pasos.
Para comenzar, inicie el servidor de desarrollo en el IDE y abra su navegador.
Diseño de la tarjeta de inicio
Hay 5 elementos que queremos construir para la carga del esqueleto: la imagen del logotipo, el título, los detalles, la imagen de portada y la sección de pie de página.
Elementos esqueléticos de Daily Dev
Ahora, agregaremos skeleton
clases a las ubicaciones de los elementos anteriores.
Para el logotipo,
<img class="card__header header__img skeleton" />
Para el título, habrá 2 divs para representar las dos líneas que tenemos en la imagen de arriba.
<div class="skeleton skeleton-text"></div>
<div class="skeleton skeleton-text"></div>
Para obtener más información, agregue el siguiente código dentro del div de la clase body__text
:
<div class="skeleton skeleton-text skeleton-text__body"></div>
Dentro del body__img
div, agregue el siguiente código:
<img class="skeleton" alt="" id="cover-img" />
Para el pie de página, agregue este código:
<div class="skeleton skeleton-text skeleton-footer"></div>
Ahora, el código HTML completo de la tarjeta:
<a class="card" id="card-link" target="_blank">
<div class="card__header">
<div>
<img class="card__header header__img skeleton" id="logo-img" alt="" />
</div>
<h3 class="card__header header__title" id="card-title">
<div class="skeleton skeleton-text"></div>
<div class="skeleton skeleton-text"></div>
</h3>
</div>
<div class="card__body">
<div class="card__body body__text" id="card-details">
<div class="skeleton skeleton-text skeleton-text__body"></div>
</div>
<div class="card__body body__img">
<img class="skeleton" alt="" id="cover-img" />
</div>
</div>
<div class="card__footer" id="card-footer">
<div class="skeleton skeleton-text skeleton-footer"></div>
</div>
</a>
Ahora, agreguemos algo de estilo para hacer los componentes del esqueleto:
.skeleton {
animation: skeleton-loading 1s linear infinite alternate;
}
@keyframes skeleton-loading {
0% {
background-color: hsl(200, 20%, 80%);
}
100% {
background-color: hsl(200, 20%, 95%);
}
}
.skeleton-text {
width: 100%;
height: 0.7rem;
margin-bottom: 0.5rem;
border-radius: 0.25rem;
}
.skeleton-text__body {
width: 75%;
}
.skeleton-footer {
width: 30%;
}
Este es el diseño resultante:
Carga de componentes de la tarjeta
Inserte una template
etiqueta entre container
y el card
elemento en el index.html
archivo.
En la imagen de arriba hay una template
etiqueta que comenté, y sí, es un elemento HTML válido ;). Se utiliza para declarar fragmentos de HTML que se pueden clonar e insertar en el documento mediante script.
<template id="card-template">
Como resultado, asegúrese de agregar la etiqueta de cierre </template>
después de la etiqueta de cierre del card
div.
Ahora veamos el código JavasScript que usaremos para clonar la plantilla de la tarjeta.
Cree una script
etiqueta justo antes del final de la body
etiqueta y agregue el siguiente código:
const container = document.querySelector(".container");
const cardTemplate = document.getElementById("card-template");
for (let i = 0; i < 10; i++) {
container.append(cardTemplate.content.cloneNode(true));
}
El código anterior toma el contenedor de la página y la plantilla de la tarjeta, y luego crea 9 clones/copias de la tarjeta (haciendo 10 en total). Luego agrega/inserta las tarjetas en el contenedor.
Interfaz de usuario de esqueleto de tarjeta clonada
Necesitamos algunos datos antes de que podamos agregar contenido a la página. Normalmente, necesitaría obtener datos con un sitio web externo, pero usaremos uno que configuré específicamente para este proyecto.
Para comenzar, cree un archivo llamado data.json
en la carpeta del proyecto.
Agregue el siguiente código al archivo JSON.
[
{
"id": 1,
"logoImage": "https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/4a287b2e7cb5499bae863f8e7137cdb4",
"title": "Writing Cleaner CSS Using BEM ",
"details": "Mar 12, 2022 · 4m read time",
"coverImage": "https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/dd19e7a56475f39ab1c38167c02c7b58",
"link": "https://israelmitolu.hashnode.dev/writing-cleaner-css-using-bem-methodology"
},
{
"id": 2,
"logoImage": "https://daily-now-res.cloudinary.com/image/upload/t_logo,f_auto/v1628412854/logos/freecodecamp",
"title": "The Beginner's Guide to Sass",
"details": "Apr 05, 2022 · 8m read time",
"coverImage": "https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/bec6719be210973098293a32dc732d1e",
"link": "https://www.freecodecamp.org/news/the-beginners-guide-to-sass/"
},
{
"id": 3,
"logoImage": "https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/devto",
"title": "I made Squid Game with Javascript",
"details": "Oct 25, 2021 · 3m read time",
"coverImage": "https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/1f947033365381cbe322ddf294ad7169",
"link": "https://dev.to/0shuvo0/i-made-squid-game-with-javascript-10j9"
},
{
"id": 4,
"logoImage": "https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/4a287b2e7cb5499bae863f8e7137cdb4",
"title": "Using Custom Cursors with Javascript for a Better User Experience",
"details": "Feb 12, 2022 · 9m read time",
"coverImage": "https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/3d056b99c95b37cd35ae5cfc6a8b38be",
"link": "https://israelmitolu.hashnode.dev/using-custom-cursors-with-javascript-for-a-better-user-experience"
},
{
"id": 5,
"logoImage": "https://daily-now-res.cloudinary.com/image/upload/t_logo,f_auto/v1628412854/logos/freecodecamp",
"title": "React Best Practices - Tips for Writing Better React Code in 2022",
"details": "Feb 03, 2022 · 31m read time",
"coverImage": "https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/5a629fff5583f9ab5f0931d14736b299",
"link": "https://www.freecodecamp.org/news/best-practices-for-react/"
},
{
"id": 6,
"logoImage": "https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/tnw",
"title": "You suck at Googling: 5 tips to improve your search skills",
"details": "Mar 31, 2022 · 4m read time",
"coverImage": "https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/e318150ae67c2083ff3585a96f366f7b",
"link": "https://thenextweb.com/news/5-tips-to-improve-your-google-search-skills"
},
{
"id": 7,
"logoImage": "https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/logrocket",
"title": "A better way of solving prop drilling in React apps",
"details": "Jan 14, 2022 · 13m read time",
"coverImage": "https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/6fe4c4060bca638b419d8b2c63d8eaf7",
"link": "https://blog.logrocket.com/solving-prop-drilling-react-apps/"
},
{
"id": 8,
"logoImage": "https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/dz",
"title": "Golang and Event-Driven Architecture",
"details": "Apr 18, 2022 · 6m read time",
"coverImage": "https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/d06eddd82c62288df6e2600bcda61579",
"link": "https://dzone.com/articles/golang-and-event-driven-architecture"
},
{
"id": 9,
"logoImage": "https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/4a287b2e7cb5499bae863f8e7137cdb4",
"title": "Introduction to Git In 16 Minutes",
"details": "Mar 18, 2021 · 8m read time",
"coverImage": "https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/3c02111a8f242f607551500432e17a78",
"link": "https://vickyikechukwu.hashnode.dev/introduction-to-git-in-16-minutes"
},
{
"id": 10,
"logoImage": "https://res.cloudinary.com/daily-now/image/upload/t_logo,f_auto/v1/logos/4a287b2e7cb5499bae863f8e7137cdb4",
"title": "How to Create a Sleek Preloader Animation Using GSAP Timeline",
"details": "Jan 25, 2022 · 7m read time",
"coverImage": "https://res.cloudinary.com/daily-now/image/upload/f_auto,q_auto/v1/posts/e238c35cb9d41dd9a5475602aef00119",
"link": "https://israelmitolu.hashnode.dev/how-to-create-a-sleek-preloader-animation-using-gsap-timeline"
}
]
Para replicar la sección de noticias de Daily Dev, hemos creado algunos datos que tienen una matriz de objetos con propiedades como identificación, imagen de logotipo, título, detalles e imagen de portada.
Agrega el siguiente código a la etiqueta del script que aloja tu JavaScript:
fetch("data.json")
.then((response) => response.json())
.then((posts) => {
container.innerHTML = "";
posts.forEach((post) => {
const div = cardTemplate.content.cloneNode(true);
div.getElementById("card-link").href = post.link;
div.getElementById("logo-img").src = post.logoImage;
div.getElementById("card-title").textContent = post.title;
div.getElementById("card-details").textContent = post.details;
div.getElementById("cover-img").src = post.coverImage;
div.getElementById(
"card-footer"
).innerHTML = ` <ion-icon name="arrow-up"></ion-icon>
<ion-icon name="chatbox-ellipses"></ion-icon>
<ion-icon name="bookmark"></ion-icon>`;
container.append(div);
});
});
El código anterior es lo que usaremos para agregar contenido a las tarjetas una vez que terminen de cargarse.
Ahora, déjame explicarte el código poco a poco:
fetch("data.json")
.then((response) => response.json())
Aquí, tenemos una solicitud de recuperación básica, donde establecemos la ruta al recurso. En este caso, el data.json
archivo. Si fuera una API externa, usaría la URL del punto final como argumento:
El fetch()
método no devuelve directamente el cuerpo de la respuesta JSON, sino una promesa que se resuelve con un objeto de respuesta.
Para obtener más información, consulta los documentos de MDN .
.then((posts) => {
container.innerHTML = "";
posts.forEach((post) => {
const div = cardTemplate.content.cloneNode(true);
div.getElementById("logo-img").src = post.logoImage;
div.getElementById("card-title").textContent = post.title;
div.getElementById("card-details").textContent = post.details;
div.getElementById("cover-img").src = post.coverImage;
div.getElementById(
"card-footer"
).innerHTML = `<ion-icon name="arrow-up"></ion-icon>
<ion-icon name="chatbox-ellipses"></ion-icon>
<ion-icon name="bookmark"></ion-icon>`;
container.append(div);
});
});
Aquí, definimos lo que debería suceder después de obtener los datos.
El código primero borra la página y luego ejecuta un forEach()
método que extrae las propiedades del archivo JSON y luego lo ingresa en los elementos de la tarjeta (imagen del logotipo, título de la tarjeta,...) usando .textContent
la propiedad.
Finalmente, para el pie de página, solíamos .innerHTML
insertar los íconos como contenido HTML.
Si agregó todo correctamente, no debería haber ningún error, y esta es nuestra interfaz de usuario de carga de esqueleto completamente funcional.
Nuestra interfaz de usuario de esqueleto de Daily Dev terminada
Consulte la demostración en vivo y el repositorio de código fuente en Github.
Es importante tener en cuenta que no establecimos un tiempo de espera porque esta pantalla básica depende de la velocidad de la red del usuario.
Si desea simularlo a diferentes velocidades de red, vaya a la pestaña de red en Devtools de su navegador.
He aquí cómo hacerlo en Chrome v100:
Acelerador de red en Chrome DevTools
Si las opciones predeterminadas no le convienen, puede crear un perfil de limitación de red personalizado seleccionando la opción en la parte superior del menú desplegable.
¡Llegaste hasta el final! Aprendió sobre la carga de esqueleto y cómo contribuye a la experiencia del usuario al crear la ilusión de velocidad al cargar datos, y ha implementado la suya propia.
Espero que hayas encontrado útil este tutorial y que sirva como un buen punto de partida para crear varias pantallas de carga de esqueleto.
Si encontró este artículo revelador, compártalo con sus amigos y su red. Además, no dude en conectarse conmigo en Twitter y mi blog donde comparto recursos y artículos para que sea un mejor desarrollador.
¡Gracias por leer y feliz codificación!
Antes de ir, aquí hay algunos paquetes de carga de esqueleto para React , Angular y Vue .
1627287240
In this video, we are going to implement skeleton loader in React application with the help of Ant design and 3rd party fake api.
#react #api #skeleton
1625068740
Learn how to create the skeleton ui animation using the react native animated api.
New To React Native?
React Native Foundation + Firebase + Redux :
https://www.udemy.com/course/react-native-foundation/?referralCode=687495CA6B80FFD08B88
Was it helpful?
Support : https://www.buymeacoffee.com/unsureprogammer
Twitter https://twitter.com/nathvarun
Instagram https://www.instagram.com/nathvarun25
Facebook https://www.facebook.com/nathvarun
Contact unsureprogrammer@gmail.com
#react native #react native animated #skeleton #ui #tutorial #firebase
1625065020
Starter Project : https://github.com/nathvarun/skeleton-ui-react-native-animated/tree/master
Learn how to create the skeleton ui animation using the react-native-reanimated api.
New To React Native?
React Native Foundation + Firebase + Redux :
https://www.udemy.com/course/react-native-foundation/?referralCode=687495CA6B80FFD08B88
Was it helpful?
Support : https://www.buymeacoffee.com/unsureprogammer
Twitter https://twitter.com/nathvarun
Instagram https://www.instagram.com/nathvarun25
Facebook https://www.facebook.com/nathvarun
Contact unsureprogrammer@gmail.com
#react native #skeleton #react native animations
1619105160
In this era of digitalization, teams are focusing more on giving best experience to the users. So, user experience keeps on evolving, new methodologies and components are being adopted by the teams. One among them is Skeleton Loader which you might have seen on many web sites and mobile apps.
While building a product, we always try to ensure great performance from an API turnaround time, design and rendering point of view. But, no matter how hard we try there will always be a point where user has to wait. To provide best experience during this waiting is of utmost importance in evaluating the performance of the whole product. You must have seen different kinds of loaders on various websites, viz., spinner, progress bar, loading message etc.
Skeleton Loader is of same kind and to keep users involved in the process.
In this article, we will learn about Skeleton loader, its purpose, usage, and points to keep in mind while designing. Also, we will go through the practical implementation later.
#html-css #skeleton #css #c #c++