For mobile lightweight HTML5 interactive mini-games (referred to as H5 light interactive), if the screen display mode is divided, it can be classified as portrait screen and landscape screen.

Screenshot of HTML5 interactive mini game case

Usually we have done the demand, mainly portrait type, and less horizontal type. For portrait scene, we will have rich experience, therefore, the main discussion of landscape scene under some points need to pay attention to, especially how to do landscape adaptation.

For H5 light interactive games, to achieve horizontal screen, mainly to solve two points: 1. No matter which direction the user is holding it, the screen should be displayed horizontally. 2. Due to the diversity of screen resolutions, landscape adaptation is required even in landscape screen to ensure that the screen can be reasonably adapted in all resolutions.

Below, we respectively elaborate how to solve these two points.

Forced landscape display

Page contents can be displayed in vertical and horizontal directions, as shown in the following figure.

Page content display mode: vertical layout and horizontal layout

For portrait H5 light interaction, pages are expected to remain vertically oriented. If the page is displayed horizontally, developers often choose to use a prompt mask for friendly prompting, allowing users to maintain the vertical experience, as shown in the figure below.

The prompt mask reminds the user to maintain the portrait experience

Similarly, a landscape H5 light interactive game can take the same approach for simple processing, where the developer prompts the user to keep the landscape experience while the content is displayed vertically.

But that is not friendly to the user experience, because this for those who are used to open the lock for the direction of the landscape function (as shown in the figure below) of iOS users, or close the screen rotation function (as shown in the figure below) of the Android platform users, they need more than one processing step – first close the vertical placement direction lock or open screen rotation, Then the horizontal handheld device.

Vertical orientation lock (iOS) and screen rotation (Android)

Therefore, it is better to force landscape display, listen for the screen resize event, and rotate the entire root container 90 degrees counterclockwise CSS3 when judging portrait, as shown below.

/ / use CSS 3 rotating on root container 90 degrees counterclockwise var detectOrient = function () {var width = document. DocumentElement. ClientWidth, height = document.documentElement.clientHeight, $wrapper = document.getElementById("J_wrapper"), style = ""; If (width >= height){// style += "width:" + width + "px; ; Style += "height:" + height + "px;" ; style += "-webkit-transform: rotate(0); transform: rotate(0);" ; style += "-webkit-transform-origin: 0 0;" ; style += "transform-origin: 0 0;" ; } else{// portrait style += "width:" + height + "px; ; style += "height:" + width + "px;" ; style += "-webkit-transform: rotate(90deg); transform: rotate(90deg);" ; Style += "-webkit-transform-origin: "+ width / 2 + "px" + width / 2 + "px "; ; style += "transform-origin: " + width / 2 + "px " + width / 2 + "px;" ; } $wrapper.style.cssText = style; } window.onresize = detectOrient; detectOrient();Copy the code

But! There is a catch: if you are developing with the CreateJS framework, you cannot rotate the root container containing the Canvas through the CSS3 approach, because this will cause the event response position of the stage elements in the Canvas to be distorted.

Instead, use the Stage’s rotation property in the CreateJS framework as follows:

If (self.isportrait) {// Portrait // stage rotation self.stage.x = self.canvasheight; Self.stage.rotation = 90; // more... }else {// landscape self.stage.x = 0; self.stage.rotation = 0; // more... }Copy the code

Landscape adaptation processing

In the face of mobile terminal multi-resolution complex and miscellaneous situation, we can say that the general situation (that is, the common vertical screen) page adaptation is familiar with the heart, but switch to the landscape screen scenario, the same page adaptation method can be directly applied? Will there be any problems?

The following author respectively from DOM and Canvas two aspects to set about how to do landscape adaptation processing.

Solve the DOM landscape adaptation problem

On the mobile end, the common mobile end adaptation scheme is REM scheme. In order to reduce the coupling between JS and CSS, the author’s team adopts VW + REM scheme when developing the page. (Students who want to know about this scheme can read “Using viewport units to achieve adaptive layout”).

The VW + REM scheme works perfectly because the scenes that the page ADAPTS tend to be portrait. But when it comes to landscape, its shortcomings are exposed.

Problems arising from the current VW unit adaptation scheme

As shown in the figure above, the maximum width of the response breakpoint will result in white space on both sides of the page, which can be resolved by removing the maximum width limit. The real disadvantage is that, due to the vw unit, the adaptation size is based on the screen width, so the larger the screen width will lead to the larger container, text, and DOM elements beyond the screen, and large text is not the user experience we want.

So, how about a fixed layout of px units?

However, the fixed layout of PX units is only suitable for some scenes. For scenes requiring full screen coverage of content (as shown in the figure below), there may be such an unsatisfactory user experience: the gap between absolutely positioned elements is too large, resulting in unattractive layout, or the gap is too small, resulting in block of stacked elements.

Px unit fixed layout adaptation scheme brings problems

As we have learned, the vw unit is characterized by the adaptation of the conversion size depending on the screen width, so in forced landscape display, we can similarly convert to screen height, that is, vw unit is replaced by VH unit.

After further improvement, satisfactory adaptation effect will be obtained, as shown in the figure below.

Better fit solution — VW, VH unit collocation

Specific implementation can refer to the following SCSS code:

$vw_base: 375; $vw_fontsize: 20; html { font-size: 20px; Font-size: ($vw_fontsize / $vw_base) * 100vw; } @media screen and (orientation: landscape) { html { font-size: 20px; font-size: ($vw_fontsize / $vw_base) * 100vh; }}Copy the code

Solve Canvas landscape adaptation problem

To solve the Canvas landscape adaptation problem, there are two mainstream solutions in practical application:

  1. By making two sets of Canvas schemes.
  2. Using the means of scaling adaptation scheme.

The approach of the two-canvas scheme is that the page contains the corresponding display of the two Canvas for vertical and horizontal screen respectively, but their data is open. However, this scheme will inevitably have limitations, and is more suitable for the game logical data processing is simple, and the stage elements are few and middle scenes;

The method of scaling adaptation is to adopt the most common scaling means — using the scale attribute of CSS3 Transform to achieve the purpose of “one design size fits multiple resolutions screen”.

Cases with different adaptation schemes

Some of the most mature HTML5 game engines on the market, such as Cocos2D, Laya, Egret, etc., have integrated landscape adaptation solutions themselves. If you look at them, they are generally adapted using the idea of scaling.

However, there is no ready-made landscape adaptation solution for the CreateJS and PixiJS frameworks we use, especially if we are developing a landscape game using native Javascript.

Therefore, let’s study how to solve the Canvas landscape adaptation problem.

Note: The sample code below is based on the CreateJS framework.

Select the appropriate zoom mode

The core of landscape adaptation is scaling. The Canvas is scaled to fit the screen window by means of scale attribute. Similar to the performance of background-size properties, scaling fit can have many modes, either clipped or unclipped, scaled by long or short edges, and so on. According to some common application scenarios, there are five commonly used scaling modes: Contain, Cover, Fill, fixed-width, and fixed-height. Depending on the actual scene requirements of the game, we can choose one of the zoom modes to adapt.

Below, we will explain the definition, implementation and applicable scenarios of each of the five scaling modes.

A. Contain mode

Canvas can be likened to a picture, and the adaptation of pictures can be associated with background-size, which is often used to adapt background pictures. Its attribute values include contain and cover.

Contain one of the ways to scale is called contain. Because in this mode, the stage content (gameArea) maintains an aspect ratio and scales to fit the browser’s visual window until it displays the full stage content.

According to the following deduction, the zoom ratio (scaleRadio) in this zoom mode is the ratio between the width or height ratio of the browser’s visible window and the game content.

Contain mode scale derivation diagram

According to the conclusion, the simple code is as follows:

Function Contain: function(){var self = this; self.radioX = self.radioY = Math.min((self.winWidth / self.designWidth) , (self.winHeight / self.designHeight)); self.canvasWidth = self.designWidth; self.canvasHeight = self.designHeight; }Copy the code

Contain mode: if the stage content does not Contain the same width/height ratio as the browser’s visual window, the stage content does not fill the browser’s visual window, and there will be empty sections on the left and right sides of the stage.

For this kind of folder mode, it is more suitable for H5 light interaction where the stage background is solid color or gradient type, so that the stage content and the immediate place of the window can naturally transition and connect without being abrupt.

B. Cover model

Similarly, one of these modes is called the cover mode by virtue of the concept of cover. In this mode, the stage content (gameArea) keeps its aspect ratio scaled to fit the browser’s visual window until the stage content fills the window.

According to the following deduction, the zoom ratio (scaleRadio) in this zoom mode is the ratio between the width or height of the browser’s visible window and the game content.

Scale derivation diagram in Cover mode

According to the conclusion, the simple code is as follows:

Function (){var self = this; self.radioX = self.radioY = Math.max((self.winWidth / self.designWidth) , (self.winHeight / self.designHeight)); self.canvasWidth = self.designWidth; self.canvasHeight = self.designHeight; }Copy the code

In Cover mode, if the aspect ratio of the stage content is not equal to the aspect ratio of the browser’s visual window, the upper, lower, or left and right sides will be clipped because the stage content needs to fill the entire browser’s visual window.

The H5 type of light interaction can be considered in Cover mode if it is possible to ensure that all of the highlighted content in the game scene is displayed and the clipped content is irrelevant.

How do you make sure that what you want to highlight doesn’t get clipped? Here comes the concept of a “safe zone”, which is the content area that will never be clipped. It should be the overlapping area created by the smallest screen viewable window (currently iPhone 4) and the largest screen viewable window (currently iPhone 7 Plus), as shown below.

Security Zone is the part inside the red dotted line box

During the design phase, communicate with the designer, product, etc., and tell them that everything they don’t want cut should be laid out in the “safe zone”.

C. the Fill pattern

Fill mode can be analogous to backgrouns-size: 100% 100% performance. In this mode, the aspect ratio is not maintained, and the width and height ratio of the stage content and the browser’s visual window are scaled until the stage content stretches and fills the window.

According to the deduction below, we can get the zoom ratio (scaleRadio) in this zoom mode, which is the ratio of the width of the game content to the width of the visible window, and the ratio of the height of the game content to the height of the visible window.

Derivation diagram of scale in Fill mode

According to the conclusion, the simple code is as follows:

Fill: function(){var self = this; self.radioX = (self.winWidth / self.stageWidth); self.radioY = (self.winHeight / self.stageHeight); self.canvasWidth = self.designWidth; self.canvasHeight = self.designHeight; }Copy the code

This mode is neither empty nor clipped, but the content is stretched to some extent when the aspect ratio of the stage content is not equal to the aspect ratio of the browser’s visual window.

Although this violent treatment eliminates the trouble of blank space and cropping, there will be stretching deformation, which depends on whether it can be accepted.

D. Fixed -width mode

Different from images, Canvas can dynamically draw size. Therefore, we can consider drawing Canvas dynamically according to screen window size changes. From the perspective of keeping the horizontal content of the stage unchanged, we propose the following model: The stage content (gameArea) is scaled to the same Width as the browser’s visible window, while the stage height (Canvas height) is redrawn to the height of the browser’s visible window, called fixed-width mode.

Based on the diagram below, we can derive the zoom ratio (scaleRadio) in this zoom mode, which is the ratio of the width of the browser’s visible window to the game content.

Scaling derivation diagram in fixed-width mode

According to the conclusion, the simple code is as follows:

// FIXED_WIDTH: function(){var self = this; self.radioX = self.radioY = self.winWidth / self.designWidth; self.canvasWidth = self.designWidth; self.canvasHeight = self.winHeight / self.radioY; }Copy the code

In fixed-width mode, regardless of the resolution, the horizontal content of the stage remains unchanged, while the vertical height will be dynamically cut, which is more suitable for those H5 light interaction types where the scene can be lengthwise expanded.

E. Fixed – Height model

In fixed-width mode, the stage content (gameArea) is scaled to the same Height as the browser’s visual window, while the stage Width (Canvas Width) is redrawn to the same Width as the browser’s visual window.

From the diagram below, we can derive the zoom ratio (scaleRadio) in this zoom mode, which is the height ratio of the browser’s visible window to the game content.

Scaling derivation diagram in fixed-height mode

According to the conclusion, the simple code is as follows:

// FIXED_HEIGHT: function(){var self = this; self.radioX = self.radioY= self.winHeight / self.designHeight; self.canvasWidth = self.winWidth / self.radioX; self.canvasHeight = self.designHeight; }Copy the code

In contrast to fixed-width mode, in fixed-height mode, the vertical content of the stage remains the same, while the horizontal Width is dynamically cropped. The application scenarios of this mode should be more extensive, such as the common type of parkour game H5 light interaction.

Added relocation and redrawing strategies

Based on the above five zoom modes, we can see that there is a possibility of clipping for Cover, fixed-width and fixed-height modes. In particular, fixed-height is a common mode for landscape games, but it will crop out when the screen is small, and we don’t want to crop out edge elements, like the music icon in the upper right corner. In fixed-width and fixed-height modes, the stage area needs to be redrawn, so some stage elements need to be resized.

So, in addition to the basic scale fit mode implementation, in order to address the need for edge elements not to be clipped and for some stage elements to be redrawn, we need to add two strategies: relocation and redrawing.

A. relocation

The principle of the trim element relocation strategy is simple. Set the top, left, right, and bottom attributes for the element to be repositioned (you can also name it another attribute). This allows us to recalculate the position during adaptation based on these custom properties and the actual Canvas size displayed.

To ensure performance, here are some things to look out for in the policy:

  1. In the stage, not all game elements need to be relocated, so we just create an array of elements that need to be relocated.
  2. Properly control the relocation times, we do not need to relocate every frame when the tick is drawn, but only need to process the relocation when the Canvas size changes.

Here is the code for the relocation strategy:

// halfCutHeight and halfCutWidth are calculated according to the actual Canvas size _setSize: function(){//... if(self.isPortrait) { // ... self.halfCutWidth = (self.canvasWidth * self.radioY - this.winWidth ) / 2 / self.radioY; self.halfCutHeight = (self.canvasHeight * self.radioX - this.winHeight) / 2 / self.radioX; }else { // ... self.halfCutWidth = (self.canvasWidth * self.radioX - this.winWidth ) / 2 / self.radioX; self.halfCutHeight = (self.canvasHeight * self.radioY - this.winHeight) / 2 / self.radioY; } / /... }, // Adjustposition: function(adjustposition){var self = this; item && self.adjustPositionArr.push(item); self.adjustPositionArr.map(function(item, index, arr){ (typeof item.top == "number") && (item.y = item.top + self.halfCutHeight >= 0 ? self.halfCutHeight : 0); (typeof item.left == "number") && (item.x = item.left + self.halfCutWidth >= 0 ? self.halfCutWidth : 0); (typeof item.bottom == "number") && (item.y = self.canvasHeight - item.getBounds().height - item.bottom + self.halfCutHeight >= 0 ? self.halfCutHeight : 0); (typeof item.right == "number") && (item.x = self.canvasWidth - item.getBounds().width - item.right - self.halfCutWidth); }); AdjustPosition: function(item){var self = this; self._adjustPosition(item); }Copy the code

B. draw again

For some elements that take gameArea as the reference standard for their size setting, the size of the stage area changes when the drawing area needs to be completed during adaptation, and accordingly, the element needs to be redrawn. This is the significance of the redrawing strategy.

Similarly, to ensure performance, the redraw strategy also needs to ensure:

  1. Create the corresponding array record full display graphics object.
  2. Do not redraw every frame tick, only redraw when fit.

Here is the code for the redraw strategy:

_adjustFullSize: function(item){var self = this; item && self.adjustFullSizeArr.push(item); self.adjustFullSizeArr.map(function(item, index, arr){ item.drawRect(0, 0, self.canvasWidth, self.canvasHeight); }); }, adjustPosition: function(item){var self = this; self._adjustPosition(item); }Copy the code

At this point, the Canvas landscape adaptation problem can be completely solved.

This part is quite long. The author briefly summarizes that a simple solution to Canvas landscape adaptation problem should include at least two aspects:

  • Select the appropriate zoom mode. The scheme has five built-in scaling modes, which can be adapted according to different scenarios in practical application.
  • Added relocation and redrawing strategies. To ensure that the edge elements are not clipped and the stage elements are dynamically rendered to fit the dynamic changes of the stage area.

The final overall effect is available
Experience the addressTo experience, click the text element to switch mode. In addition, the overall implementation is based on the CreateJS framework, the implementation of the code will host the author
githubOn.

The latter

The main focus of this article is on addressing points and solutions in landscape games, so if there are any bugs in the implementation code, feel free to correct them! Or if readers have better ideas, please share them in the comments.

The resources

  • How to Build an EFFICIENT H5
  • Screen Adaptation of Cocos2D-JS
  • Cocos2d-js Multi-resolution adaptation Scheme
  • Cocos2d-js Alignment Strategy
  • Laya Engine – Automatic Landscape Adaptation
  • Phaser-scalemanager Object
  • How to Create Mobile Games for Different Screen Sizes and Resolutions
  • Egret — Screen Adaptation Strategies