Chapter 1: A set of code · Multi-terminal operation
Authors of this chapter: @Zihuan, @Yefeng
Hi, everyone. Did you go to the cloud Computing Conference last week? I spent a day on site, and I got a lot out of it. At the same time, aliyun Hybrid Cloud Experience Camp (hereinafter referred to as “Experience Camp” or “Online Exhibition Hall”) 2.0, the online experience window of our department, was officially launched during this conference.
Compared with the original 1.0 version, the new version of the experience camp has qualitative changes in both visual and interactive experience. It effectively presents the construction/management/use and best practices of hybrid cloud products in the form of responsive graphics, 3D scenes and video transitions.
To start with a real video (click the image below to see the video) :
Specific requirements and design scheme can be referred to this article: “We moved the hybrid cloud exhibition hall online”. On the basis of this solution, we put forward a set of front-end technical solutions for multi-terminal types (PC/ touch screen/mobile phone) and multi-scene users (online access/exhibition site experience) by integrating the actual application scenarios and actual audiences. Core technologies are summarized as follows:
In this series of articles that begin with this article, we will discuss each of the steps and issues listed above, bringing a series of technical practices related to Web front-end development and cross-side experience.
Core Technical Points
The joining together of three-terminal
The new hybrid cloud experience camp will be launched in three main ways: PC, wireless and touch screen version displayed at the cloud expo.
Touch-screen version is based on the PC version of the improved version, the main difference is to jump out to take some outside the chain of qr code layer (Overlay), on the one hand, to ensure the overall application is not out, on the other hand, by making the show offline audience phone code, to realize the change of user experience from offline to online, extend the service link of the product.
For the scenario of wireless terminal, we determined the development mode of adapting PC/ touch screen + wireless terminal (mobile phone) with a set of code: not only to ensure the full presentation of PC terminal capability in the wireless terminal, but also to consider adapting the user experience and running environment of the wireless terminal.
Below, several key points and strategies in the development process of wireless terminal are introduced.
(1) Operating environment identification
In general, we judge the device type by the navigator.userAgent information provided by the browser, while different mobile apps have made certain adaptations for the common WebView kernel. The different UA information of several Android apps is given here, through which we can even know the specific App in which the Web application is running (such as wechat, Dingding, Taobao, etc.).
Mozilla/5.0 (Linux; Android 10; VOG-AL10 Build/HUAWEIVOG-AL10; Wv) AppleWebKit / 537.36 (KHTML, Like Gecko) Version/4.0 Chrome/89.0.4389.72 MQQBrowser/ 6.2TBS /045811 Mobile Safari/ 537.36mmwebid /7299 X28000f3d MicroMessenger / 8.0.15.2020 (0) the Process/tools/WeChat arm64 Weixin NetType/WIFI Language/zh_CN/ABI arm64 # nailing a version Webview ua information Mozilla/5.0 (Linux; U; Android 10; zh-CN; VOG - AL10 Build/HUAWEIVOG - AL10) AppleWebKit / 537.36 (KHTML, Like Gecko) Version/4.0 Chrome/69.0.3497.100 UWS/3.22.1.161 Mobile Safari/537.36 AliApp(DingTalk/6.3.0) Com. Alibaba. Android. Rimet / 15239879 Channel/language/useful - 227200 CN UT4Aplus Hmos / 1/0.2.25 colorScheme taobao/light # one version Webview ua information Mozilla/5.0 (Linux; U; Android 10; zh-CN; VOG - AL10 Build/HUAWEIVOG - AL10) AppleWebKit / 537.36 (KHTML, Like Gecko) Version/4.0 Chrome/69.0.3497.100 UWS/3.22.1.171 Mobile Safari/537.36 AliApp(TB/10.5.0) UCBS/2.11.1.1 TTID / 227200 @taobao_android_10.5.0 WindVane / 8.5.0 1080 x2265 UT4Aplus / 0.2.29Copy the code
To facilitate use in the React project, we wrapped a custom hook called useIsMobile:
import { useEffect, useState } from 'react';
// IsMobilejs, a popular NPM package for determining device types based on UA information, is used
import isMobile from 'ismobilejs';
// Whether to move the terminal
export const isMobileDevice = () = > isMobile().any;
// Whether it is mobile
export const isPhone = () = > isMobile().phone;
// Is it a tablet device
export const isTablet = () = > isMobile().tablet;
// Custom hook - Used to determine the device type
export function useIsMobile() {
const [deviceType, setDeviceType] = useState({
isMobile: isMobileDevice(),
isPhone: isPhone(),
isTablet: isTablet(),
});
useEffect(() = > {
const handleResize = () = > {
setDeviceType({
isMobile: isMobileDevice(),
isPhone: isPhone(),
isTablet: isTablet(),
});
};
window.addEventListener('resize', handleResize);
return () = > {
window.removeEventListener('resize', handleResize); }; } []);return deviceType;
}
Copy the code
With this hook, it is easy to implement multiple styles of a UI:
import { useIsMobile } from '@/utils/mobile';
export default function MyApp() {
const { isPhone } = useIsMobile();
return (
<div className={`my-appThe ${isPhone ? 'phone' : 'normal'} `.trim()} >{/* My UI components... * /}</div>
);
}
Copy the code
(2) Wireless end size adaptation — Rem is defined according to the design draft
Since the layout of the wireless terminal is similar to that of the PC terminal in that the width of the page is larger than the height, the CSS Transform: Rotate implements a mandatory landscape (more on this later). After some deliberation, we found it impractical to use VW/VH units for page layout in this project: considering the rotation of VW/VH often requires calculation changes, which are tedious in terms of coding efficiency and readability. A simple Rem scheme was implemented:
import { isMobileDevice } from '@/utils/mobile';
// The size of the design draft
const DESIGN_WIDTH = 1852;
// Set the rem method
export function setRem() {
// Since the landscape effect is enforced through transform:rotate
// Therefore take the longest edge of the page as the actual page width
constrealPageWidth = ! isMobileDevice() ?window.innerWidth
: Math.max(window.innerWidth, window.innerHeight);
// calculate Rem: 1rem to the page = 100px to the design
document.documentElement.style.fontSize = `The ${(100 * realPageWidth) / DESIGN_WIDTH
}px`;
}
Copy the code
Mandatory landscape
The figure above shows the display effect of the hybrid cloud experience camp in portrait mode on the mobile terminal. In order to transfer the “landscape” experience consistent with that on the PC terminal directly to the mobile terminal, it is necessary to implement a set of “forced landscape” scheme — that is, the page always takes the longest side of the display device as the “width” and the smallest side as the “height”. When the width of the device is smaller than the height, we can use CSS Transform to rotate the page by 90 degrees to achieve “forced landscape” effect.
(1) Judge landscape screen state by window size
// Determine whether the screen width is greater than the height
function isLandscape() {
const width = window.innerWidth || document.documentElement.clientWidth;
const height = window.innerHeight || document.documentElement.clientHeight;
return width > height;
}
Copy the code
(2) Based on the landscape state to achieve a mandatory landscape UI container
import React, { useEffect, useState } from 'react';
import isLandscape from '@/utils/isLandscape';
import './Orientation.scss';
// Force the simulation of mobile phone landscape display
export default function Orientation({ children }) {
const [landscape, setLandscape] = useState(isLandscape());
useEffect(() = > {
const resizeFn = () = > {
setLandscape(isLandscape());
};
window.addEventListener('resize', resizeFn);
window.addEventListener('orientationchange', resizeFn);
return () = > {
window.removeEventListener('resize', resizeFn);
window.removeEventListener('orientationchange', resizeFn); }; } []);return (
<div
className={`orientation-wrapperThe ${landscape? ' ': 'orientation-wrapper-rotate'
}`.trim()} >
{children}
</div>
);
}
Copy the code
/* Orientation.scss */
.orientation-wrapper {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
&.orientation-wrapper-rotate {
left: 100%;
width: 100vh;
height: 100vw;
transform: rotate(90deg);
transform-origin: top left; }}Copy the code
Wrapping components around the outermost layer of the page route makes the entire page automatically landscape.
Horizontal scroll
Horizontal layout and scrolling (sliding) are widely used in the new hybrid Cloud experience camp, and modal scrolling is also used in the details page, which is different from the browser’s native scrolling feature.
To wireless the cases in the video below details page, for example, graphic content always can guarantee when I scroll to the bottom section titles and the alignment TAB, which requires scrolling container according to the length of the section content dynamically calculated rolling range, it is essential for the browser’s native scrolling behavior is not convenient to implement (when the chapter content through the short, roll title is less than the top).
When implementing the cross-terminal scrolling (sliding) scheme, it is necessary to integrate the differences of devices, browsers and other environments and interactive features to solve several major problems:
(1) Horizontally scroll the mouse wheel on the PC
This is inconsistent with the browser’s native features (which typically require keyboard assist keys).
(2) Content scrolling on the mobile terminal
The CSS Transform: Rotate property is used to implement forced landscape on a mobile device in portrait mode. In this case, the sliding effect is inconsistent with the original sliding direction of the system.
(3) Different touchscreen devices have different characteristics
The implementation mechanism of touch screen events is different in browsers of different platforms, such as Windows touch screen, Android and iOS, in the Cloud Computing exhibition hall. In order to ensure the consistency of experience, a relatively low-level and unified scheme is needed.
To solve these problems, I wrapped a multi-purpose “scroll event” based on the WHEEL event on PC and the Touch event on mobile:
// PC based on wheel implementation, mobile based on touch implementation
export default function bindScrollEvent(element, callback) {
// Handle the wheel event
const handleScroll = (event) = > {
// Mask the default behavior
if (typeof event.preventDefault === 'function') {
event.preventDefault();
}
// Perform the callback
callback(event);
};
// Cursor position cache
let startX;
let startY;
// Handle touch events - touch start
const handleTouchStart = (event) = > {
const clientCoords = getClientCoords(event);
// Reset the cursor position
startX = clientCoords.x;
startY = clientCoords.y;
};
// Handle touch events - touch end
const handleTouchMove = (event) = > {
const clientCoords = getClientCoords(event);
// Perform the callback
callback({
deltaX: startX - clientCoords.x,
deltaY: startY - clientCoords.y,
});
// Update the cache
startX = clientCoords.x;
startY = clientCoords.y;
};
// Bind the wheel event
element.addEventListener('wheel', handleScroll, {
passive: false});// Bind the touch event
element.addEventListener('touchstart', handleTouchStart);
element.addEventListener('touchmove', handleTouchMove);
return () = > {
// Unbind the wheel event
element.removeEventListener('wheel', handleScroll);
// Unbind the touch event
element.removeEventListener('touchstart', handleTouchStart);
element.removeEventListener('touchmove', handleTouchMove);
};
}
// The method to obtain the cursor position, compatible with PC & mobile terminal
export function getClientCoords(event) {
if (
event &&
typeof event.clientX === 'number' &&
typeof event.clientX === 'number'
) {
return {
x: event.clientX,
y: event.clientY,
};
}
if (event.touches.length > 0) {
return {
x: event.touches[0].clientX,
y: event.touches[0].clientY,
};
}
return {
x: event.changedTouches[0].clientX,
y: event.changedTouches[0].clientY,
};
}
Copy the code
This event is used in the React component as follows:
import React, { useEffect, useRef } from 'react';
export default function MyScrollableComponent() {
const container = useRef(null);
const scrollContentOffset = useRef(0);
useEffect(() = > {
if(! container.current)return;
const handleScroll = (event) = > {
// Take the maximum displacement in the X and Y directions as the delta of the roll
const delta =
Math.abs(event.deltaX) > Math.abs(event.deltaY)
? event.deltaX
: event.deltaY;
// Cache the scroll position
scrollContentOffset.current += delta;
// Use transform: Translate to implement horizontal mode scrolling
container.current.style.transform = `translate(The ${-1 * scrollContentOffset.current
}px, 0)`;
};
// Bind the scroll event
const unbindScrollEvent = bindScrollEvent(
container.current.parentElement,
handleScroll
);
return () = > {
// Unbind the scroll eventunbindScrollEvent(); }; } []);return (
<div className="my-scrollable-component">
<div className="container" ref={container}>{/* Horizontal scrolling content... * /}</div>
</div>
);
}
Copy the code
Video playback
There are a lot of video playing scenes in the experience camp, such as home page video background, cutscene video, content video edited by operation and so on. We render the video using the
For example: in UC, X5 and other browsers, videos are rendered as a Native Layer at the top of the browser’s viewport unless otherwise specified, which is out of the reach of HTML and CSS. For a page with a video background, the effect in these browsers is that the video directly fills the screen, making it impossible for the user to manipulate anything on the page.
The control group shown below is the performance of
Fortunately, the browser kernels we’ll be adapting (X5, UC) all provide some non-standard attributes that declare how
<video
src={url}
// Standard properties, so the video does not play in full screen
webkit-playsinline="true"
playsInline
// UC kernel-specific attributes, function as above
renderer="standard"
// X5 kernel specific attributes, same as above
x5-video-player-type="h5-page"
/>
Copy the code
As noted above, most mobile browsers can render video backgrounds properly. But there are still a few browsers that are stubborn (even switching to canvas for video rendering doesn’t help, because browsers hijack video resources and render them native).
For those browsers, we had to do some downgrading of the experience: UA information was used to determine the browser type, and for those on the blacklist, the video background was downgraded to an image, and transitions were cancelled. By sacrificing part of the experience in this way, the whole user process is fully available.
Summary & Reflection
(1) Cross-end & responsive development
- In the stage of page loading and React application initialization, the browser UA determines the operating environment and loads different styles, so that the Web application has cross-terminal capability.
- In the implementation of custom hook, isPhone, isMobile, isTablet state update is driven by the window resize event. The main benefit of this is native development and debugging: when we open Chrome’s console to simulate mobile devices, the page is updated to mobile style due to the window size change triggered.
(2) Some experience and key points
-
When developing a UI component that is common to both PC and wireless terminals, it is necessary to study the design intention of the two versions first, and sort out the experience and visual similarities and differences of the components on different terminals. It is best to combine the requirements of the PC and wireless side.
-
Load multiple styles and implement some device type-specific customization logic within the same UI component based on the device type. At the same time, when adding or reconstructing any functional logic, it is necessary to cover all ends for testing, so as to avoid the impact of the requirements of one end on the experience of other ends.
-
As different devices and browsers have different resolutions, both PC and wireless styles need to consider boundary conditions and stable states to ensure consistent experience and availability of functions.
Fixation of trailer
This paper mainly introduces the solution and implementation of several key problems related to multi-terminal adaptation. Later, two articles will be introduced, respectively, from the implementation of 3d exhibition hall and performance optimization around the wireless terminal to further introduce the technical solutions and details related to the new version of the experience camp.
Like the article friends remember to continue to pay attention to us oh 👇 ~