Archive

Archive for November, 2018

A Guide to Custom Elements for React Developers

November 8th, 2018 No comments
Screenshot of end result

I had to build a UI recently and (for the first time in a long while) I didn’t have the option of using React.js, which is my preferred solution for UI these days. So, I looked at what the built-in browser APIs had to offer and saw that using custom elements (aka Web Components) may just be the remedy that this React developer needed.

Custom elements can offer the same general benefits of React components without being tied to a specific framework implementation. A custom element gives us a new HTML tag that we can programmatically control through a native browser API.

Let’s talk about the benefits of component-based UI:

  • Encapsulation – concerns scoped to that component remain in that component’s implementation
  • Reusability – when the UI is separated into more generic pieces, they’re easier to break into patterns that you’re more likely to repeat
  • Isolation – because components are designed to be encapsulated and with that, you get the added benefit of isolation, which allows you scope bugs and changes to a particular part of your application easier

Use cases

You might be wondering who is using custom elements in production. Notably:

  • GitHub is using custom elements for their modal dialogs, autocomplete and display time.
  • YouTube’s new web app is built with Polymer and web components.

Similarities to the Component API

When trying to compare React Components versus custom elements, I found the APIs really similar:

  • They’re both classes that aren’t “new” and are able that extend a base class
  • They both inherit a mounting or rendering lifecycle
  • They both take static or dynamic input via props or attributes

Demo

So, let’s build a tiny application that lists details about a GitHub repository.

Screenshot of end result

If I were going to approach this with React, I would define a simple component like this:

<Repository name="charliewilco/obsidian" />

This component takes a single prop — the name of the repository — and we implement it like this:

class Repository extends React.Component {
  state = {
    repo: null
  };

  async getDetails(name) {
    return await fetch(`https://api.github.com/repos/${name}`, {
      mode: 'cors'
    }).then(res => res.json());
  }

  async componentDidMount() {
    const { name } = this.props;
    const repo = await this.getDetails(name);
    this.setState({ repo });
  }

  render() {
    const { repo } = this.state;

    if (!repo) {
      return <h1>Loading</h1>;
    }

    if (repo.message) {
      return <div className="Card Card--error">Error: {repo.message}</div>;
    }

    return (
      <div class="Card">
        <aside>
          <img
            width="48"
            height="48"
            class="Avatar"
            src={repo.owner.avatar_url}
            alt="Profile picture for ${repo.owner.login}"
          />
        </aside>
        <header>
          <h2 class="Card__title">{repo.full_name}</h2>
          <span class="Card__meta">{repo.description}</span>
        </header>
      </div>
    );
  }
}

See the Pen React Demo – GitHub by Charles (@charliewilco) on CodePen.

To break this down further, we have a component that has its own state, which is the repo details. Initially, we set it to be null because we don’t have any of that data yet, so we’ll have a loading indicator while the data is fetched.

During the React lifecycle, we’ll use fetch to go get the data from GitHub, set up the card, and trigger a re-render with setState() after we get the data back. All of these different states the UI takes are represented in the render() method.

Defining / Using a Custom Element

Doing this with custom elements is a little different. Like the React component, our custom element will take a single attribute — again, the name of the repository — and manage its own state.

Our element will look like this:

<github-repo name="charliewilco/obsidian"></github-repo>
<github-repo name="charliewilco/level.css"></github-repo>
<github-repo name="charliewilco/react-branches"></github-repo>
<github-repo name="charliewilco/react-gluejar"></github-repo>
<github-repo name="charliewilco/dotfiles"></github-repo>

See the Pen Custom Elements Demo – GitHub by Charles (@charliewilco) on CodePen.

To start, all we need to do to define and register a custom element is create a class that extends the HTMLElement class and then register the name of the element with customElements.define().

class OurCustomElement extends HTMLElement {}
window.customElements.define('our-element', OurCustomElement);

And we can call it:

<our-element></our-element>

This new element isn’t very useful, but with custom elements, we get three methods to expand the functionality of this element. These are almost analogous to React’s lifecycle methods for their Component API. The two lifecycle-like methods most relevant to us are the disconnectedCallBack and the connectedCallback and since this is a class, it comes with a constructor.

Name Called when
constructor An instance of the element is created or upgraded. Useful for initializing state, settings up event listeners, or creating Shadow DOM. See the spec for restrictions on what you can do in the constructor.
connectedCallback The element is inserted into the DOM. Useful for running setup code, such as fetching resources or rendering UI. Generally, you should try to delay work until this time
disconnectedCallback When the element is removed from the DOM. Useful for running clean-up code.

To implement our custom element, we’ll create the class and set up some attributes related to that UI:

class Repository extends HTMLElement {
  constructor() {
    super();

    this.repoDetails = null;

    this.name = this.getAttribute("name");
    this.endpoint = `https://api.github.com/repos/${this.name}`    
    this.innerHTML = `<h1>Loading</h1>`
  }
}

By calling super() in our constructor, the context of this is the element itself and all the DOM manipulation APIs can be used. So far, we’ve set the default repository details to null, gotten the repo name from element’s attribute, created an endpoint to call so we don’t have to define it later and, most importantly, set the initial HTML to be a loading indicator.

In order to get the details about that element’s repository, we’re going to need to make a request to GitHub’s API. We’ll use fetch and, since that’s Promise-based, we’ll use async and await to make our code more readable. You can learn more about the async/await keywords here and more about the browser’s fetch API here. You can also tweet at me to find out whether I prefer it to the Axios library. (Hint, it depends if I had tea or coffee with my breakfast.)

Now, let’s add a method to this class to ask GitHub for details about the repository.

class Repository extends HTMLElement {
  constructor() {
    // ...
  }

  async getDetails() {
    return await fetch(this.endpoint, { mode: "cors" }).then(res => res.json());
  }
}

Next, let’s use the connectedCallback method and the Shadow DOM to use the return value from this method. Using this method will do something similar as when we called Repository.componentDidMount() in the React example. Instead, we’ll override the null value we initially gave this.repoDetails — we’ll use this later when we start to call the template to create the HTML.

class Repository extends HTMLElement {
  constructor() {
    // ...
  }

  async getDetails() {
    // ...
  }

  async connectedCallback() {
    let repo = await this.getDetails();
    this.repoDetails = repo;
    this.initShadowDOM();
  }

  initShadowDOM() {
    let shadowRoot = this.attachShadow({ mode: "open" });
    shadowRoot.innerHTML = this.template;
  }
}

You’ll notice that we’re calling methods related to the Shadow DOM. Besides being a rejected title for a Marvel movie, the Shadow DOM has its own rich API worth looking into. For our purposes, though, it’s going to abstract the implementation of adding innerHTML to the element.

Now we’re assigning the innerHTML to be equal to the value of this.template. Let’s define that now:

class Repository extends HTMLElement {
  get template() {
    const repo = this.repoDetails;
  
    // if we get an error message let's show that back to the user
    if (repo.message) {
      return `<div class="Card Card--error">Error: ${repo.message}</div>`
    } else {
      return `
      <div class="Card">
        <aside>
          <img width="48" height="48" class="Avatar" src="${repo.owner.avatar_url}" alt="Profile picture for ${repo.owner.login}" />
        </aside>
        <header>
          <h2 class="Card__title">${repo.full_name}</h2>
          <span class="Card__meta">${repo.description}</span>
        </header>
      </div>
      `
    }
  }
}

That’s pretty much it. We’ve defined a custom element that manages its own state, fetches its own data, and reflects that state back to the user while giving us an HTML element to use in our application.

After going through this exercise, I found that the only required dependency for custom elements is the browser’s native APIs rather than a framework to additionally parse and execute. This makes for a more portable and reusable solution with similar APIs to the frameworks you already love and use to make your living.

There are drawbacks of using this approach, of course. We’re talking about various browser support issues and some lack of consistency. Plus, working with DOM manipulation APIs can be very confusing. Sometimes they are assignments. Sometimes they are functions. Sometimes those functions take a callback and sometimes they don’t. If you don’t believe me, take a look at adding a class to an HTML element created via document.createElement(), which is one of the top five reasons to use React. The basic implementation isn’t that complicated but it is inconsistent with other similar document methods.

The real question is: does it even out in the wash? Maybe. React is still pretty good at the things it’s designed to be very very good at: the virtual DOM, managing application state, encapsulation, and passing data down the tree. There’s next to no incentive to use custom elements inside that framework. Custom elements, on the other hand, are simply available by virtue of building an application for the browser.

Learn more

The post A Guide to Custom Elements for React Developers appeared first on CSS-Tricks.

Categories: Designing, Others Tags:

Enjoy 2019 with these New Year Freebies!

November 8th, 2018 No comments
new year freebies

2019 is just around the corner and preparations for the new year have already begun.

This year, the folks over at Vexels created this cool freebie to give your designs a special touch.

With these badges, illustrations and icons, your New Year graphics will surely have a fun and unique look.
This freebie includes +130 graphics in multiple styles, and everything comes in multiple formats too!

Enjoy all of your new year freebies!

1- T-shirt

2- Party Elements

3- New Year Posters

4- Fireworks

5- Champagne

6- Balloons

7- 2019 Graphics

Categories: Others Tags:

Sharing Data Among Multiple Servers Through AWS S3

November 8th, 2018 No comments

Sharing Data Among Multiple Servers Through AWS S3

Sharing Data Among Multiple Servers Through AWS S3

Leonardo Losoviz

2018-11-08T12:30:30+01:002018-11-08T14:22:17+00:00

When providing some functionality for processing a file uploaded by the user, the file must be available to the process throughout the execution. A simple upload and save operation presents no issues. However, if in addition the file must be manipulated before being saved, and the application is running on several servers behind a load balancer, then we need to make sure that the file is available to whichever server is running the process at each time.

For instance, a multi-step “Upload your user avatar” functionality may require the user to upload an avatar on step 1, crop it on step 2, and finally save it on step 3. After the file is uploaded to a server on step 1, the file must be available to whichever server handles the request for steps 2 and 3, which may or may not be the same one for step 1.

A naive approach would be to copy the uploaded file on step 1 to all other servers, so the file would be available on all of them. However, this approach is not just extremely complex but also unfeasible: for instance, if the site runs on hundreds of servers, from several regions, then it cannot be accomplished.

A possible solution is to enable “sticky sessions” on the load balancer, which will always assign the same server for a given session. Then, steps 1, 2 and 3 will be handled by the same server, and the file uploaded to this server on step 1 will still be there for steps 2 and 3. However, sticky sessions are not fully reliable: If in between steps 1 and 2 that server crashed, then the load balancer will have to assign a different server, disrupting the functionality and the user experience. Likewise, always assigning the same server for a session may, under special circumstances, lead to slower response times from an overburdened server.

A more proper solution is to keep a copy of the file on a repository accessible to all servers. Then, after the file is uploaded to the server on step 1, this server will upload it to the repository (or, alternatively, the file could be uploaded to the repository directly from the client, bypassing the server); the server handling step 2 will download the file from the repository, manipulate it, and upload it there again; and finally the server handling step 3 will download it from the repository and save it.

In this article, I will describe this latter solution, based on a WordPress application storing files on Amazon Web Services (AWS) Simple Storage Service (S3) (a cloud object storage solution to store and retrieve data), operating through the AWS SDK.

Note 1: For a simple functionality such as cropping avatars, another solution would be to completely bypass the server, and implement it directly in the cloud through Lambda functions. But since this article is about connecting an application running on the server with AWS S3, we don’t consider this solution.

Note 2: In order to use AWS S3 (or any other of the AWS services) we will need to have a user account. Amazon offers a free tier here for 1 year, which is good enough for experimenting with their services.

Note 3: There are 3rd party plugins for uploading files from WordPress to S3. One such plugin is WP Media Offload (the lite version is available here), which provides a great feature: it seamlessly transfers files uploaded to the Media Library to an S3 bucket, which allows to decouple the contents of the site (such as everything under /wp-content/uploads) from the application code. By decoupling contents and code, we are able to deploy our WordPress application using Git (otherwise we cannot since user-uploaded content is not hosted on the Git repository), and host the application on multiple servers (otherwise, each server would need to keep a copy of all user-uploaded content.)

Creating The Bucket

When creating the bucket, we need to pay consideration to the bucket name: Each bucket name must be globally unique on the AWS network, so even though we would like to call our bucket something simple like “avatars”, that name may already be taken, then we may choose something more distinctive like “avatars-name-of-my-company”.

We will also need to select the region where the bucket is based (the region is the physical location where the data center is located, with locations all over the world.)

The region must be the same one as where our application is deployed, so that accessing S3 during the process execution is fast. Otherwise, the user may have to wait extra seconds from uploading/downloading an image to/from a distant location.

Note: It makes sense to use S3 as the cloud object storage solution only if we also use Amazon’s service for virtual servers on the cloud, EC2, for running the application. If instead, we rely on some other company for hosting the application, such as Microsoft Azure or DigitalOcean, then we should also use their cloud object storage services. Otherwise, our site will suffer an overhead from data traveling among different companies’ networks.

In the screenshots below we will see how to create the bucket where to upload the user avatars for cropping. We first head to the S3 dashboard and click on “Create bucket”:


S3 dashboard
S3 dashboard, showing all our existing buckets. (Large preview)

Then we type in the bucket name (in this case, “avatars-smashing”) and choose the region (“EU (Frankfurt)”):


Create a bucket screen
Creating a bucket through in S3. (Large preview)

Only the bucket name and region are mandatory. For the following steps we can keep the default options, so we click on “Next” until finally clicking on “Create bucket”, and with that, we will have the bucket created.

Setting Up The User Permissions

When connecting to AWS through the SDK, we will be required to enter our user credentials (a pair of access key ID and secret access key), to validate that we have access to the requested services and objects. User permissions can be very general (an “admin” role can do everything) or very granular, just granting permission to the specific operations needed and nothing else.

As a general rule, the more specific our granted permissions, the better, as to avoid security issues. When creating the new user, we will need to create a policy, which is a simple JSON document listing the permissions to be granted to the user. In our case, our user permissions will grant access to S3, for bucket “avatars-smashing”, for the operations of “Put” (for uploading an object), “Get” (for downloading an object), and “List” (for listing all the objects in the bucket), resulting in the following policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:Put*",
                "s3:Get*",
                "s3:List*"
            ],
            "Resource": [
                "arn:aws:s3:::avatars-smashing",
                "arn:aws:s3:::avatars-smashing/*"
            ]
        }
    ]
}

In the screenshots below, we can see how to add user permissions. We must go to the Identity and Access Management (IAM) dashboard:


IAM dashboard
IAM dashboard, listing all the users we have created. (Large preview)

In the dashboard, we click on “Users” and immediately after on “Add User”. In the Add User page, we choose a user name (“crop-avatars”), and tick on “Programmatic access” as the Access type, which will provide the access key ID and secret access key for connecting through the SDK:


Add user page
Adding a new user. (Large preview)

We then click on button “Next: Permissions”, click on “Attach existing policies directly”, and click on “Create policy”. This will open a new tab in the browser, with the Create policy page. We click on the JSON tab, and enter the JSON code for the policy defined above:


Create policy page
Creating a policy granting ‘Get’, ‘Post’ and ‘List’ operations on the ‘avatars-smashing’ bucket. (Large preview)

We then click on Review policy, give it a name (“CropAvatars”), and finally click on Create policy. Having the policy created, we switch back to the previous tab, select the CropAvatars policy (we may need to refresh the list of policies to see it), click on Next: Review, and finally on Create user. After this is done, we can finally download the access key ID and secret access key (please notice that these credentials are available for this unique moment; if we don’t copy or download them now, we’ll have to create a new pair):


User creation success page
After the user is created, we are offered a unique time to download the credentials. (Large preview)

Connecting To AWS Through The SDK

The SDK is available through a myriad of languages. For a WordPress application, we require the SDK for PHP which can be downloaded from here, and instructions on how to install it are here.

Once we have the bucket created, the user credentials ready, and the SDK installed, we can start uploading files to S3.

Uploading And Downloading Files

For convenience, we define the user credentials and the region as constants in the wp-config.php file:

define ('AWS_ACCESS_KEY_ID', '...'); // Your access key id
define ('AWS_SECRET_ACCESS_KEY', '...'); // Your secret access key
define ('AWS_REGION', 'eu-central-1'); // Region where the bucket is located. This is the region id for "EU (Frankfurt)"

In our case, we are implementing the crop avatar functionality, for which avatars will be stored on the “avatars-smashing” bucket. However, in our application we may have several other buckets for other functionalities, requiring to execute the same operations of uploading, downloading and listing files. Hence, we implement the common methods on an abstract class AWS_S3, and we obtain the inputs, such as the bucket name defined through function get_bucket, in the implementing child classes.

// Load the SDK and import the AWS objects
require 'vendor/autoload.php';
use AwsS3S3Client;
use AwsExceptionAwsException;

// Definition of an abstract class
abstract class AWS_S3 {
  
  protected function get_bucket() {

    // The bucket name will be implemented by the child class
    return '';
  }
}

The S3Client class exposes the API for interacting with S3. We instantiate it only when needed (through lazy-initialization), and save a reference to it under $this->s3Client as to keep using the same instance:

abstract class AWS_S3 {

  // Continued from above...

  protected $s3Client;

  protected function get_s3_client() {

    // Lazy initialization
    if (!$this->s3Client) {

      // Create an S3Client. Provide the credentials and region as defined through constants in wp-config.php
      $this->s3Client = new S3Client([
        'version' => '2006-03-01',
        'region' => AWS_REGION,
        'credentials' => [
          'key' => AWS_ACCESS_KEY_ID,
          'secret' => AWS_SECRET_ACCESS_KEY,
        ],
      ]);
    }

    return $this->s3Client;
  }
}

When we are dealing with $file in our application, this variable contains the absolute path to the file in disk (e.g. /var/app/current/wp-content/uploads/users/654/leo.jpg), but when uploading the file to S3 we should not store the object under the same path. In particular, we must remove the initial bit concerning the system information (/var/app/current) for security reasons, and optionally we can remove the /wp-content bit (since all files are stored under this folder, this is redundant information), keeping only the relative path to the file (/uploads/users/654/leo.jpg). Conveniently, this can be achieved by removing everything after WP_CONTENT_DIR from the absolute path. Functions get_file and get_file_relative_path below switch between the absolute and the relative file paths:

abstract class AWS_S3 {

  // Continued from above...

  function get_file_relative_path($file) {

    return substr($file, strlen(WP_CONTENT_DIR));
  }

  function get_file($file_relative_path) {

    return WP_CONTENT_DIR.$file_relative_path;
  }
}

When uploading an object to S3, we can establish who is granted access to the object and the type of access, done through the access control list (ACL) permissions. The most common options are to keep the file private (ACL => “private”) and to make it accessible for reading on the internet (ACL => “public-read”). Because we will need to request the file directly from S3 to show it to the user, we need ACL => “public-read”:

abstract class AWS_S3 {

  // Continued from above...

  protected function get_acl() {

    return 'public-read';
  }
}

Finally, we implement the methods to upload an object to, and download an object from, the S3 bucket:

abstract class AWS_S3 {

  // Continued from above...

  function upload($file) {

    $s3Client = $this->get_s3_client();

    // Upload a file object to S3
    $s3Client->putObject([
      'ACL' => $this->get_acl(),
      'Bucket' => $this->get_bucket(),
      'Key' => $this->get_file_relative_path($file),
      'SourceFile' => $file,
    ]);
  }

  function download($file) {

    $s3Client = $this->get_s3_client();

    // Download a file object from S3
    $s3Client->getObject([
      'Bucket' => $this->get_bucket(),
      'Key' => $this->get_file_relative_path($file),
      'SaveAs' => $file,
    ]);
  }
}

Then, in the implementing child class we define the name of the bucket:

class AvatarCropper_AWS_S3 extends AWS_S3 {

  protected function get_bucket() {

    return 'avatars-smashing';
  }
}

Finally, we simply instantiate the class to upload the avatars to, or download from, S3. In addition, when transitioning from steps 1 to 2 and 2 to 3, we need to communicate the value of $file. We can do this by submitting a field “file_relative_path” with the value of the relative path of $file through a POST operation (we don’t pass the absolute path for security reasons: no need to include the “/var/www/current” information for outsiders to see):

// Step 1: after the file was uploaded to the server, upload it to S3. Here, $file is known
$avatarcropper = new AvatarCropper_AWS_S3();
$avatarcropper->upload($file);

// Get the file path, and send it to the next step in the POST
$file_relative_path = $avatarcropper->get_file_relative_path($file);
// ...

// --------------------------------------------------

// Step 2: get the $file from the request and download it, manipulate it, and upload it again
$avatarcropper = new AvatarCropper_AWS_S3();
$file_relative_path = $_POST['file_relative_path'];
$file = $avatarcropper->get_file($file_relative_path);
$avatarcropper->download($file);

// Do manipulation of the file
// ...

// Upload the file again to S3
$avatarcropper->upload($file);

// --------------------------------------------------

// Step 3: get the $file from the request and download it, and then save it
$avatarcropper = new AvatarCropper_AWS_S3();
$file_relative_path = $_REQUEST['file_relative_path'];
$file = $avatarcropper->get_file($file_relative_path);
$avatarcropper->download($file);

// Save it, whatever that means
// ...

Displaying The File Directly From S3

If we want to display the intermediate state of the file after manipulation on step 2 (e.g. the user avatar after cropped), then we must reference the file directly from S3; the URL couldn’t point to the file on the server since, once again, we don’t know which server will handle that request.

Below, we add function get_file_url($file) which obtains the URL for that file in S3. If using this function, please make sure that the ACL of the uploaded files is “public-read”, or otherwise it won’t be accessible to the user.

abstract class AWS_S3 {

  // Continue from above...

  protected function get_bucket_url() {

    $region = $this->get_region();

    // North Virginia region is simply "s3", the others require the region explicitly
    $prefix = $region == 'us-east-1' ? 's3' : 's3-'.$region;

    // Use the same scheme as the current request
    $scheme = is_ssl() ? 'https' : 'http';

    // Using the bucket name in path scheme
    return $scheme.'://'.$prefix.'.amazonaws.com/'.$this->get_bucket();
  }

  function get_file_url($file) {

    return $this->get_bucket_url().$this->get_file_relative_path($file);
  }
}

Then, we can simply we get the URL of the file on S3 and print the image:

printf(
  "<img src='%s'>",
  $avatarcropper->get_file_url($file)
);

Listing Files

If in our application we want to allow the user to view all previously uploaded avatars, we can do so. For that, we introduce function get_file_urls which lists the URL for all the files stored under a certain path (in S3 terms, it’s called a prefix):

abstract class AWS_S3 {

  // Continue from above...

  function get_file_urls($prefix) {

    $s3Client = $this->get_s3_client();

    $result = $s3Client->listObjects(array(
      'Bucket' => $this->get_bucket(),
      'Prefix' => $prefix
    ));

    $file_urls = array();
    if(isset($result['Contents']) && count($result['Contents']) > 0 ) {

      foreach ($result['Contents'] as $obj) {

        // Check that Key is a full file path and not just a "directory"
        if ($obj['Key'] != $prefix) { 

          $file_urls[] = $this->get_bucket_url().$obj['Key'];
        }
      }
    }

    return $file_urls;
  }
}

Then, if we are storing each avatar under path “/users/${user_id}/“, by passing this prefix we will obtain the list of all files:

$user_id = get_current_user_id();
$prefix = "/users/${user_id}/";
foreach ($avatarcropper->get_file_urls($prefix) as $file_url) {
  printf(
    "<img src='%s'>", 
    $file_url
  );
}

Conclusion

In this article, we explored how to employ a cloud object storage solution to act as a common repository to store files for an application deployed on multiple servers. For the solution, we focused on AWS S3, and proceeded to show the steps needed to be integrated into the application: creating the bucket, setting-up the user permissions, and downloading and installing the SDK. Finally, we explained how to avoid security pitfalls in the application, and saw code examples demonstrating how to perform the most basic operations on S3: uploading, downloading and listing files, which barely required a few lines of code each. The simplicity of the solution shows that integrating cloud services into the application is not difficult, and it can also be accomplished by developers who are not much experienced with the cloud.

Smashing Editorial(rb, ra, yk, il)
Categories: Others Tags:

5 Principles of Cuteness in Web Design

November 8th, 2018 No comments

Okay, how in the heck do you make something “cute”? Now maybe you’ve grown up in an environment where getting in touch with your feelings was anathema. Maybe you just naturally gravitated to Max Steel over Hello Kitty. Maybe you’ve never had a problem with any of this, and freely make high-pitched noises whenever there’s a puppy in sight (as is appropriate).

Whatever your background, attempting to turn a web design into “something cute” will require you to try and determine exactly what makes a thing cute in the first place, and that’s where it gets complicated. The whole concept is naturally subjective. It’s a vaguely warm and fuzzy emotion that seems directly connected to our impulse to smile, but what brings it on can vary greatly. There are people who call insects cute, whereas I could never bring myself to agree.

They’re just icky, okay?

But whatever you use, cuteness sells—cue the obligatory joke about cat GIFs on the Internet. At some point in our lives, we will be called upon to make something cute, because that’s what the client wants, and we’ll start idly wondering if that means making everything pink.

Don’t do that. Read this instead.

1. Imagery

Using cute imagery is almost cheating. I mean, you’re just taking something cute, throwing it into your design, and calling it a day. But don’t discount the idea; being cheap and easy doesn’t mean that it’s not effective. Some curmudgeons notwithstanding, human beings are biologically wired to get hit right in the pleasure centers of our brains when we see human offspring (particularly our own), and other things that share some baby-like features (ie. puppies, kittens, pandas).

Stock photography can be a great start, but keep in mind that illustration can be a great way to trigger the same responses that wild make people want to squeeze your website. If you need to use original imagery, hiring an illustrator is also way easier than getting babies or animals to hold still.

Example: Wonderbly uses both photos and illustration.

2. Colors

Imagery is good, but what if you want to embed that warm and fuzzy feeling into the very DNA of your design, and not just in the content? This is where we turn to color. Now repeat after me: Pink is NOT the only cute color.

In fact, “cute” colors are a concept that will vary according to individual cultures. For one thing, babies vary in color. The general rule to follow, though, boils down to three words: primaries and pastels. Bright primary colors and their softer pastel counterparts dominate the “cute aesthetic” worldwide. If you’re designing something cute for (very young) kids, use bolder tones. If you’re designing something for adults, use the pastels, which are far easier on the eyes, and tired brains.

Example: Kapu Toys goes hard on the primary colors.

3. The Other Kinds of Cute

So far, I’ve discussed cuteness in terms of human offspring and related things. Thing is, the word “cute” wasn’t even originally used for babies so much as for keen or sharp-witted people. It evolved over time to be used for attractive young people, and then finally for babies and other baby-like things.

Since then, it has also come to encompass things that, while not baby-related, still produce a warm and fuzzy kind of feeling. Things that produce feelings of comfort, contentment, and “home” can also be represented by the word “cute”.

For example: “What a cute tablecloth!” “That’s an adorable rocking chair.” Cute arrangements of flowers. etc.

Example: Noritake shows how the domestic-cute concept has reached far beyond Western borders.

The additional forms of cuteness go beyond the domestic and into the world of fashion as well. A certain warmth and contentment can be found in knowing that you’re looking good today, hence the use of phrases like, “That’s a cute dress!” This use of the word also hearkens back to its use in describing generally attractive people.

Example: Bando provides a picture-perfect example of fashion-cute.

4. Cuteness in the copy

So we’ve discussed the use of cuteness in imagery, in color, and the overall feel of a design. So how do you write cute things in your website without—for the love of God—going full “baby talk”? Well it all comes down to the emotions you want to evoke. Cuteness is a term that covers a lot of possible subject matter, but one primary emotion: warmth.

You don’t want to write the word “cute” over and over in an effort to convince people to see your page’s subject your way. You want to convey the comfort of a home where you are loved, the depth in the round and trusting eyes of a puppy, the clumsy antics of a cat that’s been surprised by something, or the sheer joy of wearing an outfit that makes you give yourself the old up-and-down glance in the mirror.

Concepts like “elegance” are inherently visual, and so designing them into a website is a purely visual exercise. Concepts like “cuteness” are far more emotional. Though you can tune the visual characteristics of just about anything to make it cute, it’s worth remembering that the emotional side of the concept can actually get pretty complex, and your copy should reflect that.

5. Cuteness in the details

Keep in mind that you don’t always need to saturate your website with cute vibes. As many parents whose children love the Minions might tell you, too much “cuteness” can get on the nerves after a while. For most websites that don’t strictly relate to children or home decorating, you’ll want to splash some cuteness in here and there, without overwhelming the user.

One of my favorite examples will always be sites like Zurb. Most of the site is fairly corporate-standard, but there’s a touch of cuteness in there, with their hidden cows, their project mascots, and entertaining bits of microcopy. It’s a light touch, but it’s enough to make them stand out.

Add Realistic Chalk and Sketch Lettering Effects with Sketch’it – only $5!

Source

Categories: Designing, Others Tags:

Simplify Styling with Functional CSS

November 7th, 2018 No comments

There is no doubt that “functional CSS” resonates strongly with some people. If that term is new to you, I belive it’s come to mean the same thing as “Atomic CSS” as defined by John Polacek here. Harry Nicholls likens it to a function that can only produce one result (although I’d call that a pure function or pure component), but instead of a return value being entirely predictable based on inputs, it is an application of style that only does one thing.

I’m of two minds here. People say how fast they can work this way. Great! They like how predictable the applied styles are. Great! I can understand how a tiny stylesheet that doesn’t grow over time is appealing as well.

At the same time, I haven’t seen writing about other styling concerns. What happens with big redesigns? Is it about the same, time- and difficulty-wise, or do you spend more time tearing down all those classes? What happens when you need a style that isn’t available? Write your own? Or does that ruin the spirit of all this and put you in dangerous territory? How intense can all the class names get? I can think of areas I’ve styled that have three or more media queries that dramatically re-style an element. Putting all that information in HTML seems like it could get awfully messy. Is consistency harder or easier? I get that “p5” might be a useful way to apply an abstract amount of padding, but you still need to sprinkle it all over your codebase and know when to use it, right?

The closest I’ve been to being convinced about it was hearing from Adam just how configurable Tailwind is. In any case, I find this all endless fascinating and, if you love it, more power to ya.

Direct Link to ArticlePermalink

The post Simplify Styling with Functional CSS appeared first on CSS-Tricks.

Categories: Designing, Others Tags:

Building a Donut Chart with Vue and SVG

November 7th, 2018 No comments

Mmm… forbidden donut.”

– Homer Simpson

I recently needed to make a donut chart for a reporting dashboard at work. The mock-up that I got looked something like this:

My chart had a few basic requirements. It needed to:

  • Dynamically calculate its segments based on an arbitrary set of values
  • Have labels
  • Scale well across all screen sizes and devices
  • Be cross-browser compatible back to Internet Explorer 11
  • Be accessible
  • Be reusable across my work’s Vue.js front end

I also wanted something that I could animate later if I needed to. All of this sounded like a job for SVG.

SVGs are accessible out-of-the-box (the W3C has a whole section on this) and can be made more accessible through additional input. And, because they’re powered by data, they’re a perfect candidate for dynamic visualization.

There are plenty of articles on the topic, including two by Chris (here and here) and a super recent one by Burke Holland. I didn’t use D3 for this project because the application didn’t need the overhead of that library.

I created the chart as a Vue component for my project, but you could just as easily do this with vanilla JavaScript, HTML, and CSS.

Here’s the finished product:

See the Pen Vue Donut Chart – Final Version by Salomone Baquis (@soluhmin) on CodePen.

Reinventing the wheel circle

Like any self-respecting developer, the first thing I did was Google to see if someone else had already made this. Then, like same said developer, I scrapped the pre-built solution in favor of my own.

The top hit for “SVG donut chart” is this article, which describes how to use stroke-dasharray and stroke-dashoffset to draw multiple overlaid circles and create the illusion of a single segmented circle (more on this shortly).

I really like the overlay concept, but found recalculating both stroke-dasharray and stroke-dashoffset values confusing. Why not set one fixed stroke-dasharrary value and then rotate each circle with a transform? I also needed to add labels to each segment, which wasn’t covered in the tutorial.

Drawing a line

Before we can create a dynamic donut chart, we first need to understand how SVG line drawing works. If you haven’t read Jake Archibald’s excellent Animated Line Drawing in SVG. Chris also has a good overview.

Those articles provide most of the context you’ll need, but briefly, SVG has two presentation attributes: stroke-dasharray and stroke-dashoffset.

stroke-dasharray defines an array of dashes and gaps used to paint the outline of a shape. It can take zero, one, or two values. The first value defines the dash length; the second defines the gap length.

stroke-dashoffset, on the other hand, defines where the set of dashes and gaps begins. If the stroke-dasharray and the stroke-dashoffset values are the length of the line and equal, the entire line is visible because we’re telling the offset (where the dash-array starts) to begin at the end of the line. If the stroke-dasharray is the length of the line, but the stroke-dashoffset is 0, then the line is invisible because we’re offsetting the rendered part of the dash by its entire length.

Chris’ example demonstrates this nicely:

See the Pen Basic Example of SVG Line Drawing, Backward and Forward by Chris Coyier (@chriscoyier) on CodePen.

How we’ll build the chart

To create the donut chart’s segments, we’ll make a separate circle for each one, overlay the circles on top of one another, then use stroke, stroke-dasharray, and stroke-dashoffset to show only part of the stroke of each circle. We’ll then rotate each visible part into the correct position, creating the illusion of a single shape. As we do this, we’ll also calculate the coordinates for the text labels.

Here’s an example demonstrating these rotations and overlays:

See the Pen Circle Overlays by Salomone Baquis (@soluhmin) on CodePen.

Basic setup

Let’s start by setting up our structure. I’m using x-template for demo purposes, but I’d recommend creating a single file component for production.

<div id="app">
  <donut-chart></donut-chart>
</div>
<script type="text/x-template" id="donutTemplate">
  <svg height="160" width="160" viewBox="0 0 160 160">
    <g v-for="(value, index) in initialValues">
      <circle :cx="cx" :cy="cy" :r="radius" fill="transparent" :stroke="colors[index]" :stroke-width="strokeWidth" ></circle>
      <text></text>
    </g>
  </svg>
</script>
Vue.component('donutChart', {
  template: '#donutTemplate',
  props: ["initialValues"],
  data() {
    return {
      chartData: [],
      colors: ["#6495ED", "goldenrod", "#cd5c5c", "thistle", "lightgray"],
      cx: 80,
      cy: 80,                      
      radius: 60,
      sortedValues: [],
      strokeWidth: 30,    
    }
  }  
})
new Vue({
  el: "#app",
  data() {
    return {
      values: [230, 308, 520, 130, 200]
    }
  },
});

With this, we:

  • Create our Vue instance and our donut chart component, then tell our donut component to expect some values (our dataset) as props
  • Establish our basic SVG shapes: for the segments and for the labels, with the basic dimensions, stroke width, and colors defined
  • Wrap these shapes in a element, which groups them together
  • Add a v-for loop to the g> element, which we’ll use to iterate through each value that the component receives
  • Create an empty sortedValues array, which we’ll use to hold a sorted version of our data
  • Create an empty chartData array, which will contain our main positioning data

Circle length

Our stroke-dasharray should be the length of the entire circle, giving us an easy baseline number which we can use to calculate each stroke-dashoffset value. Recall that the length of a circle is its circumference and the formula for circumference is 2?r (you remember this, right?).

We can make this a computed property in our component.

computed: {
  circumference() {
    return 2 * Math.PI * this.radius
  }
}

…and bind the value to our template markup.

<svg height="160" width="160" viewBox="0 0 160 160">
  <g v-for="(value, index) in initialValues">
    <circle :cx="cx" :cy="cy" :r="radius" fill="transparent" :stroke="colors[index]" :stroke-width="strokeWidth" :stroke-dasharray="circumference" ></circle>
    <text></text>
  </g>
</svg>

In the initial mockup, we saw that the segments went from largest to smallest. We can make another computed property to sort these. We’ll store the sorted version inside the sortedValues array.

sortInitialValues() {
  return this.sortedValues = this.initialValues.sort((a,b) => b-a)
}

Finally, in order for these sorted values to be available to Vue before the chart gets rendered, we’ll want to reference this computed property from the mounted() lifecycle hook.

mounted() {
  this.sortInitialValues                
}

Right now, our chart look like this:

See the Pen Donut Chart – No Segments by Salomone Baquis (@soluhmin) on CodePen.

No segments. Just a solid-colored donut. Like HTML, SVG elements are rendered in the order that they appear in the markup. The color that appears is the stroke color of the last circle in the SVG. Because we haven’t added any stroke-dashoffset values yet, each circle’s stroke goes all the way around. Let’s fix this by creating segments.

Creating segments

To get each of the circle segments, we’ll need to:

  1. Calculate the percentage of each data value from the total data values that we pass in
  2. Multiply this percentage by the circumference to get the length of the visible stroke
  3. Subtract this length from the circumference to get the stroke-offset

It sounds more complicated than it is. Let’s start with some helper functions. We first need to total up our data values. We can use a computed property to do this.

dataTotal() {
  return this.sortedValues.reduce((acc, val) => acc + val)
},

To calculate the percentage of each data value, we’ll need to pass in values from the v-for loop that we created earlier, which means that we’ll need to add a method.

methods: {
  dataPercentage(dataVal) {
    return dataVal / this.dataTotal
  }
},

We now have enough information to calculate our stroke-offset values, which will establish our circle segments.

Again, we want to: (a) multiply our data percentage by the circle circumference to get the length of the visible stroke, and (b) subtract this length from the circumference to get the stroke-offset.

Here’s the method to get our stroke-offsets:

calculateStrokeDashOffset(dataVal, circumference) {
  const strokeDiff = this.dataPercentage(dataVal) * circumference
  return circumference - strokeDiff
},

…which we bind to our circle in the HTML with:

:stroke-dashoffset="calculateStrokeDashOffset(value, circumference)"

And voilà! We should have something like this:

See the Pen Donut Chart – No Rotations by Salomone Baquis (@soluhmin) on CodePen.

Rotating segments

Now the fun part. All of segments begin at 3 o’clock, which is the default starting point for SVG circles. To get them in the right place, we need to rotate each segment to its correct position.

We can do this by finding each segment’s ratio out of 360 degrees and then offset that amount by the total degrees that came before it.

First, let’s add a data property to keep track of the offset:

angleOffset: -90,

Then our calculation (this is a computed property):

calculateChartData() {
  this.sortedValues.forEach((dataVal, index) => {
    const data = {
      degrees: this.angleOffset,
    }
    this.chartData.push(data)
    this.angleOffset = this.dataPercentage(dataVal) * 360 + this.angleOffset
  })
},

Each loop creates a new object with a “degrees” property, pushes that into our chartValues array that we created earlier, and then updates the angleOffset for the next loop.

But wait, what’s up with the -90 value?

Well, looking back at our original mockup, the first segment is shown at the 12 o’clock position, or -90 degrees from the starting point. By setting our angleOffset at -90, we ensure that our largest donut segment starts from the top.

To rotate these segments in the HTML, we’ll use the transform presentation attribute with the rotate function. Let’s create another computed property so that we can return a nice, formatted string.

returnCircleTransformValue(index) {
  return rotate(${this.chartData[index].degrees}, ${this.cx}, ${this.cy})`
},

The rotate function takes three arguments: an angle of rotation and x and y coordinates around which the angle rotates. If we don’t supply cx and cy coordinates, then our segments will rotate around the entire SVG coordinate system.

Next, we bind this to our circle markup.

:transform="returnCircleTransformValue(index)"

And, since we need to do all of these calculations before the chart is rendered, we’ll add our calculateChartData computed property in the mounted hook:

mounted() {
  this.sortInitialValues
  this.calculateChartData
}

Finally, if we want that sweet, sweet gap between each segment, we can subtract two from the circumference and use this as our new stroke-dasharray.

adjustedCircumference() {
  return this.circumference - 2
},
:stroke-dasharray="adjustedCircumference"

Segments, baby!

See the Pen Donut Chart – Segments Only by Salomone Baquis (@soluhmin) on CodePen.

Labels

We have our segments, but now we need to create labels. This means that we need to place our elements with x and y coordinates at different points along the circle. You might suspect that this requires math. Sadly, you are correct.

Fortunately, this isn’t the kind of math where we need to apply Real Concepts; this is more the kind where we Google formulas and don’t ask too many questions.

According to the Internet, the formulas to calculate x and y points along a circle are:

x = r cos(t) + a
y = r sin(t) + b

…where r is the radius, t is the angle, and a and b are the x and y center point offsets.

We already have most of this: we know our radius, we know how to calculate our segment angles, and we know our center offset values (cx and cy).

There’s one catch, though: in those formulas, t is in *radians*. We’re working in degrees, which means that we need to do some conversions. Again, a quick search turns up a formula:

radians = degrees * (π / 180)

…which we can represent in a method:

degreesToRadians(angle) {
  return angle * (Math.PI / 180)
},

We now have enough information to calculate our x and y text coordinates:

calculateTextCoords(dataVal, angleOffset) {
  const angle = (this.dataPercentage(dataVal) * 360) / 2 + angleOffset
  const radians = this.degreesToRadians(angle)

  const textCoords = {
    x: (this.radius * Math.cos(radians) + this.cx),
    y: (this.radius * Math.sin(radians) + this.cy)
  }
  return textCoords
},

First, we calculate the angle of our segment by multiplying the ratio of our data value by 360; however, we actually want half of this because our text labels are in the middle of the segment rather than the end. We need to add the angle offset like we did when we created the segments.

Our calculateTextCoords method can now be used in the calculateChartData computed property:

calculateChartData() {
  this.sortedValues.forEach((dataVal, index) => {
    const { x, y } = this.calculateTextCoords(dataVal, this.angleOffset)        
    const data = {
      degrees: this.angleOffset,
      textX: x,
      textY: y
    }
    this.chartData.push(data)
    this.angleOffset = this.dataPercentage(dataVal) * 360 + this.angleOffset
  })
},

Let’s also add a method to return the label string:

percentageLabel(dataVal) {
  return `${Math.round(this.dataPercentage(dataVal) * 100)}%`
},

And, in the markup:

<text :x="chartData[index].textX" :y="chartData[index].textY">{{ percentageLabel(value) }}</text>

Now we’ve got labels:

See the Pen Donut Chart – Unformatted Labels by Salomone Baquis (@soluhmin) on CodePen.

Blech, so off-center. We can fix this with the text-anchor presentation attribute. Depending on your font and font-size, you may want to adjust the positioning as well. Check out dx and dy for this.

Revamped text element:

<text text-anchor="middle" dy="3px" :x="chartData[index].textX" :y="chartData[index].textY">{{ percentageLabel(value) }}</text>

Hmm, it seems that if we have small percentages, the labels go outside of the segments. Let’s add a method to check for this.

segmentBigEnough(dataVal) {
  return Math.round(this.dataPercentage(dataVal) * 100) > 5
}
<text v-if="segmentBigEnough(value)" text-anchor="middle" dy="3px" :x="chartData[index].textX" :y="chartData[index].textY">{{ percentageLabel(value) }}</text>

Now, we’ll only add labels to segments larger than 5%.

And we’re done! We now have a reusable donut chart component that can accept any set of values and create segments. Super cool!

The finished product:

See the Pen Vue Donut Chart – Final Version by Salomone Baquis (@soluhmin) on CodePen.

Next steps

There are lots of ways that we can modify or improve this now that it’s built. For example:

  • Adding elements to enhance accessibility, such as </code> and <code></code> tags, aria-labels, and aria role attributes.</li> <li>Creating <strong>animations</strong> with CSS or libraries like <a target="_blank" href="https://greensock.com/">Greensock</a> to create eye-catching effects when the chart comes into view.</li> <li>Playing with <strong>color schemes</strong>.</li> </ul> <p>I’d love to hear what you think about this implementation and other experiences you’ve had with SVG charts. Share in the comments!</p> <p>The post <a target="_blank" rel="nofollow" href="https://css-tricks.com/building-a-donut-chart-with-vue-and-svg/">Building a Donut Chart with Vue and SVG</a> appeared first on <a target="_blank" rel="nofollow" href="https://css-tricks.com/">CSS-Tricks</a>.</p> <div class="fixed"></div> </div> <div class="under"> <span class="categories">Categories: </span><span><a href="http://www.webmastersgallery.com/category/design/" rel="category tag">Designing</a>, <a href="http://www.webmastersgallery.com/category/uncategorized/" rel="category tag">Others</a></span> <span class="tags">Tags: </span><span></span> </div> </div> <div class="post" id="post-1918635"> <h2><a class="title" href="http://www.webmastersgallery.com/2018/11/07/how-microsoft-dynamics-ax-is-changing-the-way-you-look-to-manufacturing-businesses/" rel="bookmark">How Microsoft Dynamics AX is Changing the Way You Look to Manufacturing Businesses</a></h2> <div class="info"> <span class="date">November 7th, 2018</span> <span class="author"></span> <span class="comments"><a href="http://www.webmastersgallery.com/2018/11/07/how-microsoft-dynamics-ax-is-changing-the-way-you-look-to-manufacturing-businesses/#respond">No comments</a></span> <div class="fixed"></div> </div> <div class="content"> <div class="ftpimagefix" style="float:left"><a target="_blank" href="http://feedproxy.google.com/~r/Noupe/~3/FoYwsLd4l1Q/how-microsoft-dynamics-ax-is-changing-the-way-you-look-to-manufacturing-businesses.html"><img decoding="async" src="https://www.noupe.com/wp-content/uploads/2018/12/Manufacturing-Businesses-1024x576.jpg" alt="Manufacturing Businesses"></a></div> <div></div> <p>Digital transformation is one of the biggest change that is happening all over the world. Now, a large portion of the world is connected in the internet loop.</p> <p> Information is exchanging on a large scale between people and our past generations never expected such drastic change will occur. We are living in that dream world where anything is possible with the help of technology. Digital transformation is the key thing in today’s world and the manufacturing industry is no exception.</p> <p>Many leading tech companies are providing the most advanced tech solutions to various sectors to increase the overall growth of human civilization. Every day, a new technology is introduced to the world and new processes and new technologies are rolling out continuously. But dealing with the large population of the world and manufacturing the products to meeting the demands of people is quite a challenging task. Human has limited potential to carry out certain tasks.</p> <h2><strong>Key Choices Around Using Dynamics AX to Manage a Manufacturing Business</strong></h2> <p>The big manufacturers are under pressure to remain at the top-level productivity throughout their supply chains in the face of changing industrial landscape. Delivering best services and improving efficiency is a top priority for many manufacturing businesses, but if manufacturers are to build an organization which can truly tackle such huge demand, they need to have a vigorous business management solution at their foundation.</p> <p>The Microsoft Dynamics suite has long been a favorite of manufacturing organizations. Microsoft Dynamics AX has come a long way from a being a by-product of collaboration between IBM company. The journey of this company is really impressive. In the current time, Dynamics AX has grown to become one of the most widely used ERP systems across the globe and supporting numerous small and large companies operate efficiently and match the changing demands of a dynamic market.</p> <h2><strong>What are the key reasons you should choose Microsoft Dynamics AX for your manufacturing business?</strong></h2> <p>Manufacturing is mainly a very process-heavy industry, but now more than ever manufacturers need to work to enhance their customers experience to be able to excel. Microsoft Dynamics AX is an integrated, adaptable business management solution in the Microsoft Dynamics line that manages the customer relationship and supply chain processes. By the help of this software tool, your people can work efficiently and make important decisions and confidence.it can help you run your business across locations and countries by consolidating and standardizing processes, provides visibility across your organization.</p> <figure><img decoding="async" src="https://www.noupe.com/wp-content/uploads/2018/12/Key-Choices-Around-Using-Dynamics-AX-to-Manage-a-Manufacturing-Business-1024x512.png" alt=""></figure> <h2><strong>Why you should choose this amazing business manufacturing software tool?</strong></h2> <p><strong>This is the best software to help people work fast and smart and provides many benefits describes as follows:</strong></p> <ul> <li>Mainly focus on business rather than on learning new applications with a solution that looks and feels like familiar Microsoft user experience.</li> <li>Give people access to real-time key performance indicators, and scorecard.</li> <li>Enable key decision-makers to work with critical information using PerformancePoint Server without logging into Microsoft Dynamics AX.</li> <li>Expand decision making with business intelligence and reporting tools that assist people to analyze data through dashboard KPIs</li> <li>Build strong connections with employees, customers, and partners using integrated and collaborative tools.</li> <li>Expand easily across borders with country-specific functionality and based on consumer requirements, including capabilities for multiple languages and currencies.</li> <li>Manage and monitor the overall performance across your organization with sophisticated reporting and analysis tools.</li> <li>Save the time and effort with the workflows that enforce and enhance control based on specific risk.</li> <li>Reduce the cost and uncertainty of protecting corporate compliance data by taking advantage of the power Microsoft Dynamics AX and other Microsoft products in your IT infrastructure.</li> </ul> <p>The whole point of improving processes, streamlining workflows and boosting the loyalty of the customer is, of course, to make your business more profitable. By providing business manufacturers with the tools and intelligence to make their services better, faster and more customer-centric. So this software fulfills all these aspirations that will result in higher profits and a healthier future.</p> <p><a target="_blank" href="https://www.noupe.com/?feed-stats-url=aHR0cHM6Ly93d3cubmV4c29mdHN5cy5jb20vdGVjaG5vbG9naWVzL21pY3Jvc29mdC1keW5hbWljcy1heC1kZXZlbG9wbWVudC5odG1s&feed-stats-url-post-id=106818">Microsoft Dynamics AX Development</a> is an intuitive, cloud-hosted intelligent solution which offers users a single common platform through which to manage their entire operations, making collaborations easier, process faster, and reporting more pragmatic. Through digitization with Dynamics AX, manufacturers can find automated digital solutions to practical issues, such as inventory monitoring, quality control, and equipment maintenance.</p> <div class="fixed"></div> </div> <div class="under"> <span class="categories">Categories: </span><span><a href="http://www.webmastersgallery.com/category/uncategorized/" rel="category tag">Others</a></span> <span class="tags">Tags: </span><span></span> </div> </div> <div class="post" id="post-1808608"> <h2><a class="title" href="http://www.webmastersgallery.com/2018/11/07/how-to-design-a-payment-form-your-customers-will-actually-complete/" rel="bookmark">How to design a payment form your customers will actually complete</a></h2> <div class="info"> <span class="date">November 7th, 2018</span> <span class="author"></span> <span class="comments"><a href="http://www.webmastersgallery.com/2018/11/07/how-to-design-a-payment-form-your-customers-will-actually-complete/#respond">No comments</a></span> <div class="fixed"></div> </div> <div class="content"> <div class="ftpimagefix" style="float:left"><a target="_blank" href="http://feedproxy.google.com/~r/Noupe/~3/BbaRbNVNmlk/design-smart-payment-forms.html"><img decoding="async" src="https://www.noupe.com/wp-content/uploads/2018/11/rawpixel-626044-unsplash-compressor-1024x819.jpg" alt="Mobile Payment Form"></a></div> <div></div> <blockquote> <p><em>70% of all shopping carts are abandoned. And 28% of those carts are abandoned because of a lengthy checkout process.</em></p> <p>— <a target="_blank" href="https://www.noupe.com/?feed-stats-url=aHR0cHM6Ly9iYXltYXJkLmNvbS9saXN0cy9jYXJ0LWFiYW5kb25tZW50LXJhdGU%3D&feed-stats-url-post-id=106707">source</a></p> </blockquote> <p>Businesses want people to spend money.</p> <p>That means paying for something should be the easiest part of the whole experience but poor payment systems continue to result in a loss of billions of dollars for businesses.</p> <p><span>A</span> <strong>successful payment experience requires a clear path</strong> that users can navigate through smoothly. After all, payment forms are the digital equivalent of a physical sale terminal in a shop or restaurant. So, it’s vital that they are slick, quick and efficient: no queueing, no obstacles, no errors.</p> <p>Creating a payment form used to be a tedious headache of a task, especially for startups and small businesses.</p> <p>But today’s tools empower users to set up a powerful, beautiful payment form integration in minutes — without a single line of code. Plus, you don’t have to pay additional fees for transactions.</p> <h2><strong>Wait — what is a payment form?</strong></h2> <p>It’s the digital version of a cash desk. <a target="_blank" href="https://www.jotform.com/help/323-Mastering-Payment-Form-Integrations-with-JotForm" rel="noopener">It authorizes online payment</a>, validates the user’s details, ensures the funds are available and pays you.</p> <h2><strong>What can you do with payment integrations?</strong></h2> <p>The options are (almost) endless. First and foremost, you can sell products or services. You can also apply complex calculations to these sales, such as adding taxes and shipping costs or subtracting coupons.</p> <p>You can give your products helpful descriptions like images, quantity, color and size options.</p> <h2>And when it comes to payment form types, the options vary.</h2> <p>You can create order forms for a single or multiple products as well as for collecting payments of a fixed amount. You can also make these payments recurrent with a subscription service.</p> <figure> <p><figure><img decoding="async" loading="lazy" src="https://cdn-images-1.medium.com/max/800/0*tCMPD_3fYEKEZskG" alt="JotForm Online Subscription" width="800" height="430"><figcaption>Here is an example of a subscription form built using JotForm Cards templates</figcaption></figure><figcaption></figcaption></p> </figure> <p>Payment forms can be used to collect donations as well.</p> <figure> <p><figure><img decoding="async" loading="lazy" src="https://cdn-images-1.medium.com/max/800/0*GaFwowjBRm5YrV94" alt="Donation Amount JotForm Online Form" width="800" height="500"><figcaption>Donation Form — Card Form Layout</figcaption></figure><figcaption></figcaption></p> </figure> <h2>How do you design smarter payment forms?</h2> <p>Here are 5 payment practices we’ve found helpful for our 4.2 million users at <a target="_blank" href="https://www.jotform.com/" rel="noopener">JotForm</a>, the online form builder company I work at.</p> <h3>1. Keep it simple</h3> <p>Numerous studies have concluded that visually complex design is seen as generally unappealing.</p> <p>And psychologically, we feel inclined to interact with a page that’s emptier.</p> <p>The length of your form also matters. For instance, JotForm’s <a target="_blank" href="https://www.jotform.com/form-templates/?classic" rel="noopener">Classic Forms</a> display all the questions, plus the payment section, on one page. This set-up works perfectly for some of our users, especially in the case of shorter forms.</p> <figure> <p><figure><img decoding="async" loading="lazy" src="https://cdn-images-1.medium.com/max/800/0*S93WmZ1ia4wnJ_jQ" alt="JotForm Online Form" width="800" height="429"><figcaption>JotForm — Classic Form</figcaption></figure><figcaption></figcaption></p> </figure> <p>But when a larger amount of detail is required, lots of information on one page can feel overwhelming.</p> <p><span>T</span>his is one of the reasons we built <a target="_blank" href="https://www.jotform.com/cards/" rel="noopener">JotForm Cards</a> as an alternative to Classic Forms. It has a one-question-per-page layout: nothing is displayed on the page apart from the question and the commands.</p> <figure> <p><figure><img decoding="async" loading="lazy" src="https://cdn-images-1.medium.com/max/800/1*uzYCJ04qmEa48y0QVPXl0g.png" alt="JotForm Product Order Form Email Section" width="800" height="344"><figcaption>Product Order Form</figcaption></figure> </p> </figure> <p>As soon as the user has completed the form, they move smoothly onto the payment.</p> <p>Both types of forms shine under different circumstances. Whatever form option you choose, there should be plenty of white space and no unnecessary fields.</p> <h3><strong>2. Offer multiple methods of payment</strong></h3> <p>Have you ever finished a meal only to be informed the restaurant only accepts cash? Instead of digesting your food, you have to walk to the nearest ATM or ask your friend for a loan. Annoying.</p> <p>We humans are always looking for ease and convenience, especially when deciding whether or not to buy something.</p> <p>And choice — whether or not we need it — matters to us subconsciously. Humans value the ability to make decisions for themselves, which is why flexibility is something to prioritize.</p> <p>The same logic applies when it comes to offering your customers multiple payment options on your order forms.</p> <p>Most of the online form builder software offer multiple integrations. At JotForm, <a target="_blank" href="https://apps.jotform.com/category/payment_processing" rel="noopener">we offer 27+ payment gateway integrations</a>, across multiple companies and payment methods.</p> <p>Anyone who wants to pay should be able to do so. It’s simple, really.</p> <h3><strong>3. Reduce delays</strong></h3> <p>Payment forms are a bit like obstacle courses. Every (metaphorical) hoop to jump through and wall to climb slows the user down, making them less likely to finish.</p> <p>Some obstacles are avoidable: like arbitrary formatting rules or password requirements. Some of them — like typos — aren’t so easily avoided.</p> <p>With JotForm Cards, we try to reduce these accidental setbacks. For instance, it will recover the email address of a user who has entered the domain name wrong <strong>e.g., john@gnail.com should be john@gmail.com</strong>.</p> <p>And when a general error has been made — like a missed field or incorrect input — it warns users with a shaking micro-animation.</p> <p>Another avoidable cause of delay? Users having to decipher meaning. <a target="_blank" href="https://www.jotform.com/blog/build-high-converting-online-forms/" rel="noopener">Make sure language is crystal clear</a> and questions are phrased in a way that makes sense.</p> <p>The quicker a user progresses to the finish line, the more likely they are to cross it by completing the payment.</p> <ul> <li>Fewer delays = quicker forms.</li> <li>Quicker forms = more conversions.</li> <li>It’s a win-win.</li> </ul> <h3><strong>4. Ask for less information</strong></h3> <p>Optional fields are the enemy of conversions.</p> <p>Baymard Institute analyzed checkout forms and found that a too long <a target="_blank" href="https://baymard.com/blog/checkout-flow-average-form-fields" rel="noopener">checkout process</a> is one of the most significant reasons users abandon a purchase, with the average checkout containing 15 form fields.</p> <p>And a massive <a target="_blank" href="https://www.jotform.com/blog/jotform-cards/" rel="noopener">50%</a> of sites ask for the same information twice, which guarantees dwindling attention spans and irritation.</p> <p>Asking for information with all content displayed in easily-digestible chunks can be helpful.</p> <h3><strong>5. Prioritize security</strong></h3> <p>We want to feel safe and secure in the places we pull out our wallets. So, smart businesses go to great lengths to make their customers feel comfortable from the moment they walk through the doors.</p> <p>Payment forms are no different. When users are expected to enter sensitive information like credit card details, they’re hyper-sensitive to anything that seems suspicious:</p> <blockquote> <p>A recent <a target="_blank" href="https://www.pwc.es/es/publicaciones/retail-y-consumo/assets/total-retail-2016.pdf" rel="noopener">study</a> revealed 17% of shoppers left a page without paying due to security concerns.</p> </blockquote> <p>Clear, efficient and professional payment forms put users at ease. Anything that looks DIY will have the opposite effect.</p> <p>That’s why you should be careful when building a payment form from scratch — tiny errors or inconsistencies will make users extra-cautious and put them off. Put users off extra-cautious.</p> <p>It also helps to enable SSL on your forms to help protect data. Visitors get peace of mind knowing all interactions are encrypted.</p> <h2><strong>Creating a payment form</strong></h2> <p>In case you are interested in building a payment form using JotForm, you can choose <a target="_blank" href="https://www.jotform.com/form-templates/?cards" rel="noopener">one of our templates</a> or create a new payment form <a target="_blank" href="https://www.jotform.com/" rel="noopener">from scratch here</a>.</p> <p>We offer two form layouts:</p> <h3><strong>1. Classic Form:</strong></h3> <p><a target="_blank" href="https://www.jotform.com/form-templates/?classic" rel="noopener">Classic Form</a> offers a more conventional layout with all questions on the same page.</p> <figure><img decoding="async" src="https://cdn-images-1.medium.com/max/800/0*eCphTsogYtyXkGUv"></figure> <h3><strong>2. Card Form:</strong></h3> <p><a target="_blank" href="https://www.jotform.com/form-templates/?cards" rel="noopener">Card Form</a> offers a website-like shopping experience, letting users proceed to purchase swiftly with a one-question-per-page format.</p> <p>Steps like add to card, address, card info, and products are shown in separate cards. Shoppers can see, and edit, their shopping bag intuitively.</p> <p><strong>Here’s how it looks:</strong></p> <h4><strong>1. Shoppers select their products.</strong></h4> <figure> <p><figure><img decoding="async" loading="lazy" src="https://cdn-images-1.medium.com/max/800/0*qoxtCmpSXpzPd6pf" alt="" width="800" height="431"><figcaption>Product List and My Bag Button</figcaption></figure><figcaption></figcaption></p> </figure> <h4><strong>2. Shoppers remove or add products if necessary, then continue.</strong></h4> <figure> <p><figure><img decoding="async" loading="lazy" src="https://cdn-images-1.medium.com/max/800/0*Try0dwQr36C9jnRn" alt="" width="800" height="429"><figcaption>Editing Shopping Bag</figcaption></figure><figcaption></figcaption></p> </figure> <p>You can easily customize your forms to match your brand by adding your logo and selecting a background color or image.</p> <h2><strong>How to add a payment integration</strong></h2> <p><strong>Step 1:</strong> Add your fields and select your payment gateway.</p> <figure><img decoding="async" src="https://cdn-images-1.medium.com/max/800/0*WDj0BKQQX0ln4Mbf"></figure> <p><strong>Step 2:</strong> Enter integration credentials using a connect button, or enter them directly.</p> <figure><img decoding="async" src="https://cdn-images-1.medium.com/max/800/0*uZcG5c6xSagiDqOm"></figure> <p><strong>Step 3:</strong> Add product image, product details like quantity, color, size.</p> <figure><img decoding="async" src="https://cdn-images-1.medium.com/max/800/0*LVg-FKUZVG6--d9o"></figure> <p><strong>Step 4:</strong> Calculate coupons, taxes, and shipping.</p> <p>You can use <a target="_blank" href="https://www.jotform.com/help/460-How-to-Create-Payment-Form-with-Purchase-Order" rel="noopener">purchase order integration</a> to check for the details and options for a generic payment field creation (it doesn’t require any credentials because it doesn’t create a real transaction).</p> <p>You can check <a target="_blank" href="https://www.jotform.com/help/514-How-to-integrate-Sofort-with-your-form" rel="noopener">Sofort integration guide</a> as an example to walk through an entire payment process.</p> <p><strong>Step 5:</strong> Write personal thank you message-autoresponder emails.</p> <figure><img decoding="async" src="https://cdn-images-1.medium.com/max/800/0*-ihb2UNpg1JXk4Uy"></figure> <p>All set. Now you can easily sell your products on websites, blogs or social media with our smart embed feature.</p> <div class="fixed"></div> </div> <div class="under"> <span class="categories">Categories: </span><span><a href="http://www.webmastersgallery.com/category/uncategorized/" rel="category tag">Others</a></span> <span class="tags">Tags: </span><span></span> </div> </div> <div class="post" id="post-1808721"> <h2><a class="title" href="http://www.webmastersgallery.com/2018/11/07/10-tips-for-building-a-visual-language/" rel="bookmark">10 Tips for Building a Visual Language</a></h2> <div class="info"> <span class="date">November 7th, 2018</span> <span class="author"><a href="http://www.webmastersgallery.com/author/admin/" title="Posts by admin" rel="author">admin</a></span> <span class="comments"><a href="http://www.webmastersgallery.com/2018/11/07/10-tips-for-building-a-visual-language/#respond">No comments</a></span> <div class="fixed"></div> </div> <div class="content"> <div class="ftpimagefix" style="float:left"><a target="_blank" href="https://www.webdesignerdepot.com/2018/11/10-tips-for-building-a-visual-language/"><img decoding="async" src="https://www.webdesignerdepot.com/cdn-origin/uploads/2018/10/mcd.jpg" title="" alt=""></a></div> <p> A visual language is just like any other form of communication.</p> <p>Where “language” refers to spoken or written communication, visual language goes a step further to connect with a certain community. Elements from color to style to type of photos or illustrations establish what a brand or company is. These visual elements tie together a group in a structured and conventional way that people understand.</p> <p>A visual language includes both the written and spoken elements of a website or brand, as well as every design technique, photo, icon, logo and item, users can see on the screen. Often users won’t know a visual language is there; they just know that when they see your website or brand they recognize it.</p> <p>Here’s how you create a visual language that users can understand.</p> <h1>1. Build a Color Palette</h1> <p>A strong color palette is an identification tool for users. When consistent colors, which as well-known and connected with the brand or website, are used; users know where they are. They aren’t lost in the massive universe of the web, because you have told them visually what website or brand they are interacting with.</p> <p>This color palette shouldn’t be unique to your website. It needs to be universally applied and connected with everything a brand does—online, social media, swag items or packaging, business cards.</p> <p>Further, the color palette should reinforce brand values.</p> <p>Think about the colors for fast-food restaurant McDonald’s: Yellow and red. The colors represent happiness (yellow) and hunger (red). How often have you ever seen this brand without the “Golden Arches” or something with red on the packaging?</p> </p> <h1>2. Create a Typographic Hierarchy</h1> <p>Just as important as selecting fonts for your brand is determining how they will be used.</p> <p>A typographic hierarchy sets the tone for whether you talk to users, whisper (small light typography) or yell at them (large all caps lettering). There’s no right or wrong way to communicate; it’s knowing what works with your tribe.</p> <p>The trick to building a typographic scale is that each level should be different enough from the one preceding and following it so it is easy to see changes in text.</p> <p>Airbnb has a distinct typographic scale with sizes and weights for typography that’s standardized. This standardization also extends to the color palette and spacing to contribute to a solid visual language.</p> <p><a target="_blank" href="https://airbnb.design/building-a-visual-language/"><img decoding="async" src="https://www.webdesignerdepot.com/cdn-origin/uploads/2018/10/airbnb.jpg" width="650" title="" alt=""></a></p> <h1>3. Establish a Grid</h1> <p>Spacing and placement of elements can be just as important as the elements themselves. Is your visual identity clean and organized or a little more chaotic?</p> <p>Your grid answers this question.</p> <p><a target="_blank" href="https://medium.freecodecamp.org/design-language-system-why-your-team-needs-one-and-how-to-built-it-d996ba8c3889">FreeCodeCamp</a> explains this structure well:</p> <blockquote> <p> A modular structure offers the possibility to work with entire engineering teams. Thus it could quickly produce products suitable for all platforms. If you are creating conceptual designs from a message sense, the integration method will be more useful. </p> </blockquote> <p>In a nutshell, what does the complexity or simplicity of the grid communicate about you?</p> <h1>4. Create a Library of Components</h1> <p>From buttons to icons to cards or popups, create a library of components so all uses of elements within the design are consistent.</p> <p>A button shouldn’t be a blue circle on one page and a red square on another. An icon shouldn’t be line style on one page and full color on another. This inconsistency can make users question if they have left your site by mistake.</p> <p>A good library of components includes all the elements needed to build pages within the design and rule for usage—size and scale for each device size and adjustments for iOS versus Android devices.</p> <p>While building common components can seem like a big chore on the front end, it can make creating new pages a breeze later down the line.</p> <h1>5. Emphasize an Image Style</h1> <p>Whether you are using photos, video or illustration, creating and maintaining an image style is important. This is one of the strongest visual assets that users will remember and associate with your website or brand.</p> <p>This style relates to anything from photo crops—tight versus wide, use of filters, speed of videos and style of composition.</p> <p>Look at the multiple elements from <a target="_blank" href="https://smashmallow.com/">Smashmallow</a> below. Even if they weren’t grouped, they contain common elements that connect the images to the brand. (Not the hot air balloon on every image.) This image style set the tone for who this company is and how they connect with users.</p> <p><a target="_blank" href="https://smashmallow.com/"><img decoding="async" src="https://www.webdesignerdepot.com/cdn-origin/uploads/2018/10/smashmallow.jpg" width="650" title="" alt=""></a></p> <h1>6. Build Rules for Animation</h1> <p>Animated elements should also follow a basic set of rules. Do they move fast or slow? Do they move on their own or only as a hover state?</p> <p>These interactions help establish visual identity as well. Animations can set a tone for a project. Using, or not using, them can say a lot about who you are and what you want to be.</p> <p>This <a target="_blank" href="https://dribbble.com/shots/4818487-Mojito">mojito shot from Dribbble</a>, below, has circular motion. Think about how other animations in the same visual language would look. They might also move in the same circular manner and at a similar speed.</p> <p><a target="_blank" href="https://www.webdesignerdepot.com/10-tips-for-building-a-visual-language"><img decoding="async" src="https://www.webdesignerdepot.com/cdn-origin/uploads7/10-tips-for-building-a-visual-language/mojito.gif" width="650" title="" alt=""></a></p> <h1>7. Match Words and Visuals</h1> <p>For a visual language to be effective, it has to match the actual language used in the design. If the words and visual elements are a mismatch, users could be confused by the experience.</p> <p>Elements should work together to create an overall package of communication that all emphasizes the same thing.</p> <p>Steffany, below, uses color, a feminine logo and words to show what her website is all about. The pieces work together to create an overall picture and don’t compete with different text and visual messages.</p> <p><a target="_blank" href="https://steffany.ua/"><img decoding="async" src="https://www.webdesignerdepot.com/cdn-origin/uploads/2018/10/steffany.jpg" width="650" title="" alt=""></a></p> <h1>8. Be True to Yourself</h1> <p>A visual language will only work if it is believable. It must represent who and what you are.</p> <p>Could you imagine Sesame Street without the characters? It would be quite difficult.</p> <p>The entire visual identity of the brand is built around Big Bird and Elmo and others. Every bit of the design reflects this. To further connect the visual identity of the television show and cartoon characters, note how both are used in the website design. This helps pull fantasy and reality together visually to help maintain the brand’s identity.</p> <p><a target="_blank" href="https://www.sesamestreet.org/"><img decoding="async" src="https://www.webdesignerdepot.com/cdn-origin/uploads/2018/10/sesame.jpg" width="650" title="" alt=""></a></p> <h1>9. Be Consistent</h1> <p>Once you’ve developed a set of guidelines for a visual language, stick to them. The biggest misstep when working to build a visual language is inconsistency.</p> <p><a target="_blank" href="https://medium.freecodecamp.org/design-language-system-why-your-team-needs-one-and-how-to-built-it-d996ba8c3889">FreeCodeCamp</a> notes that a design language should answer the following questions. They are also the benchmarks of whether you are honoring consistency with your visuals.</p> <ul> <li>What color are we using?</li> <li>Where is the logo placed?</li> <li>Was this pattern used somewhere else?</li> <li>What’s the latest documentation?</li> <li>How do we build this animation?</li> <li>Is this from the component library?</li> </ul> <p>Note that <a target="_blank" href="http://reports.corby.ca/2018/index.php">Corby</a> can affirm the answers to these questions. The logo is in a common placement, consistent colors are used throughout and the patterns/animations are used in the same way.</p> <p><a target="_blank" href="http://reports.corby.ca/2018/index.php"><img decoding="async" src="https://www.webdesignerdepot.com/cdn-origin/uploads/2018/10/corby.jpg" width="650" title="" alt=""></a></p> <h1>10. Develop a Style Guide…And Enforce it</h1> <p>Establish a visual language and codify it.</p> <p>Then put someone in charge of enforcing it. Your identity is too important to leave to chance. Humans are innately visual; visual language can be one of the strongest elements that connects users to your website or brand.</p> <p><a target="_blank" href="https://www.titleist.com/"><img decoding="async" src="https://www.webdesignerdepot.com/cdn-origin/uploads/2018/10/titelist.jpg" width="650" title="" alt=""></a></p> <table width="100%"> <tr> <td valign="center"> <a target="_blank" href="https://www.mightydeals.com/deal/sketchit.html?ref=inwidget"><b>Add Realistic Chalk and Sketch Lettering Effects with Sketch’it – only $5!</b></a> </td> <td width="90"> <a target="_blank" href="http://www.mightydeals.com/?ref=inwidget"><br /><img decoding="async" loading="lazy" src="https://mightydeals.com/web/images/widget-logo.png" height="40" width="90" border="0" title="" alt=""><br /></a> </td> </tr> </table> <p><a target="_blank" href="https://www.webdesignerdepot.com/2018/11/10-tips-for-building-a-visual-language/">Source</a></p> <div class="fixed"></div> </div> <div class="under"> <span class="categories">Categories: </span><span><a href="http://www.webmastersgallery.com/category/design/" rel="category tag">Designing</a>, <a href="http://www.webmastersgallery.com/category/uncategorized/" rel="category tag">Others</a></span> <span class="tags">Tags: </span><span></span> </div> </div> <div class="post" id="post-1809058"> <h2><a class="title" href="http://www.webmastersgallery.com/2018/11/06/how-to-build-a-virtual-reality-model-with-a-real-time-cross-device-preview/" rel="bookmark">How To Build A Virtual Reality Model With A Real-Time Cross-Device Preview</a></h2> <div class="info"> <span class="date">November 6th, 2018</span> <span class="author"></span> <span class="comments"><a href="http://www.webmastersgallery.com/2018/11/06/how-to-build-a-virtual-reality-model-with-a-real-time-cross-device-preview/#respond">No comments</a></span> <div class="fixed"></div> </div> <div class="content"> <div class="ftpimagefix" style="float:left"><a target="_blank" href="http://feedproxy.google.com/~r/SmashingMagazine/~3/3jl3JxF7JsE/"></a></div> <p> <title>How To Build A Virtual Reality Model With A Real-Time Cross-Device Preview

    How To Build A Virtual Reality Model With A Real-Time Cross-Device Preview

    Alvin Wan

    2018-11-06T23:50:16+01:002018-11-07T10:45:16+00:00

    Virtual reality (VR) is an experience based in a computer-generated environment; a number of different VR products make headlines and its applications range far and wide: for the winter Olympics, the US team utilized virtual reality for athletic training; surgeons are experimenting with virtual reality for medical training; and most commonly, virtual reality is being applied to games.

    We will focus on the last category of applications and will specifically focus on point-and-click adventure games. Such games are a casual class of games; the goal is to point and click on objects in the scene, to finish a puzzle. In this tutorial, we will build a simple version of such a game but in virtual reality. This serves as an introduction to programming in three dimensions and is a self-contained getting-started guide to deploying a virtual reality model on the web. You will be building with webVR, a framework that gives a dual advantage — users can play your game in VR and users without a VR headset can still play your game on a phone or desktop.

    Developing For Virtual Reality

    Any developer can create content for VR nowadays. To get a better understanding of VR development, working a demo project can help. Read article ?

    In the second half of these tutorial, you will then build a “mirror” for your desktop. This means that all movements the player makes on a mobile device will be mirrored in a desktop preview. This allows you see what the player sees, allowing you to provide guidance, record the game, or simply keep guests entertained.

    Prerequisites

    To get started, you will need the following. For the second half of this tutorial, you will need a Mac OSX. Whereas the code can apply to any platform, the dependency installation instructions below are for Mac.

    • Internet access, specifically to glitch.com;
    • A virtual reality headset (optional, recommended). I use Google Cardboard, which is offered at $15 a piece.

    Step 1: Setting Up A Virtual Reality (VR) Model

    In this step, we will set up a website with a single static HTML page. This allows us to code from your desktop and automatically deploy to the web. The deployed website can then be loaded on your mobile phone and placed inside a VR headset. Alternatively, the deployed website can be loaded by a standalone VR headset. Get started by navigating to glitch.com. Then,

    1. Click on “New Project” in the top-right.
    2. Click on “hello-express” in the drop-down.

    Get started by navigating to glitch.com
    (Large preview)

    Next, click on views/index.html in the left sidebar. We will refer to this as your “editor”.


    The next step would be to click on views/index.html in the left sidebar which will be referred to this as your “editor”.
    (Large preview)

    To preview the webpage, click on “Preview” in the top left. We will refer to this as your preview. Note that any changes in your editor will be automatically reflected in this preview, barring bugs or unsupported browsers.


    To preview the webpage, click on “Preview” in the top left. We will refer to this as your preview. Note that any changes in your editor will be automatically reflected in this preview, barring bugs or unsupported browsers.
    (Large preview)

    Back in your editor, replace the current HTML with the following boilerplate for a VR model.

    <!DOCTYPE html>
    <html>
      <head>
          <script src="https://aframe.io/releases/0.7.0/aframe.min.js"></script>
      </head>
      <body>
        <a-scene>
          
          <!-- blue sky -->
          <a-sky color="#a3d0ed"></a-sky>
          
          <!-- camera with wasd and panning controls -->
          <a-entity camera look-controls wasd-controls position="0 0.5 2" rotation="0 0 0"></a-entity>
            
          <!-- brown ground -->
          <a-box shadow id="ground" shadow="receive:true" color="#847452" width="10" height="0.1" depth="10"></a-box>
            
          <!-- start code here -->
          <!-- end code here -->
        </a-scene>
      </body>
    </html>
    

    Navigate see the following.


    When navigating back to your preview, you will see the bakground colors blue and brown.
    (Large preview)

    To preview this on your VR headset, use the URL in the omnibar. In the picture above, the URL is https://point-and-click-vr-game.glitch.me/. Your working environment is now set up; feel free to share this URL with family and friends. In the next step, you will create a virtual reality model.

    Step 2: Build A Tree Model

    You will now create a tree, using primitives from aframe.io. These are standard objects that Aframe has pre-programmed for ease of use. Specifically, Aframe refers to objects as entities. There are three concepts, related to all entities, to organize our discussion around:

    1. Geometry and material,
    2. Transformation Axes,
    3. Relative Transformations.

    First, geometry and material are two building blocks of all three-dimensional objects in code. The geometry defines the “shape” — a cube, a sphere, a pyramid, and so on. The material defines static properties of the shape, such as color, reflectiveness, roughness.

    Aframe simplifies this concept for us by defining primitives, such as , , and many others to make a specification of a geometry and its material simpler. Start by defining a green sphere. On line 19 in your code, right after , add the following.

    
          <!-- start code here -->
          <a-sphere color="green" radius="0.5"></a-sphere>  <!-- new line -->
          <!-- end code here -->
    

    Second, there are three axes to transform our object along. The x axis runs horizontally, where x values increase as we move right. The y axis runs vertically, where y values increase as we move up. The z axis runs out of your screen, where z values increase as we move towards you. We can translate, rotate, or scale entities along these three axes.

    For example, to translate an object “right,” we increase its x value. To spin an object like a top, we rotate it along the y-axis. Modify line 19 to move the sphere “up” — this means you need to increase the sphere’s y value. Note that all transformations are specified as , meaning to increase its y value, you need to increase the second value. By default, all objects are located at position 0, 0, 0. Add the position specification below.

    
          <!-- start code here -->
          <a-sphere color="green" radius="0.5" position="0 1 0"></a-sphere> <!-- edited line -->
          <!-- end code here -->
    

    Third, all transformations are relative to its parent. To add a trunk to your tree, add a cylinder inside of the sphere above. This ensures that the position of your trunk is relative to the sphere’s position. In essence, this keeps your tree together as one unit. Add the entity between the and tags.

    
          <a-sphere color="green" radius="0.5" position="0 1 0">
            <a-cylinder color="#84651e" position="0 -0.9 0" radius="0.05"></a-cylinder> <!-- new line -->
          </a-sphere>
    

    To make this treeless barebones, add more foliage, in the form of two more green spheres.

    
          <a-sphere color="green" radius="0.5" position="0 0.75 0">
            <a-cylinder color="#84651e" position="0 -0.9 0" radius="0.05"></a-cylinder>
            <a-sphere color="green" radius="0.35" position="0 0.5 0"></a-sphere> <!-- new line -->
            <a-sphere color="green" radius="0.2" position="0 0.8 0"></a-sphere> <!-- new line -->
          </a-sphere>
    

    Navigate back to your preview, and you will see the following tree:


    When navigating back to your preview, you will now be able to see a green tree placed in your background.
    (Large preview)

    Reload the website preview on your VR headset, and check out your new tree. In the next section, we will make this tree interactive.

    Step 3: Add Click Interaction To Model

    To make an entity interactive, you will need to:

    • Add an animation,
    • Have this animation trigger on click.

    Since the end user is using a virtual reality headset, clicking is equivalent to staring: in other words, stare at an object to “click” on it. To effect these changes, you will start with the cursor. Redefine the camera, by replacing line 13 with the following.

    <a-entity camera look-controls wasd-controls position="0 0.5 2" rotation="0 0 0">
      <a-entity cursor="fuse: true; fuseTimeout: 250"
                position="0 0 -1"
                geometry="primitive: ring; radiusInner: 0.02; radiusOuter: 0.03"
                material="color: black; shader: flat"
                scale="0.5 0.5 0.5"
                raycaster="far: 20; interval: 1000; objects: .clickable">
        <!-- add animation here -->
      </a-entity>
    </a-entity>
    

    The above adds a cursor that can trigger the clicking action. Note the objects: .clickable property. This means that all objects with the class “clickable” will trigger the animation and receive a “click” command where appropriate. You will also add an animation to the click cursor, so that users know when the cursor triggers a click. Here, the cursor will shrink slowly when pointing at a clickable object, snapping after a second to denote an object has been clicked. Replace the comment with the following code:

    <a-animation begin="fusing" easing="ease-in" attribute="scale"
      fill="backwards" from="1 1 1" to="0.2 0.2 0.2" dur="250"></a-animation>
    

    Move the tree to the right by 2 units and add class “clickable” to the tree, by modifying line 29 to match the following.

    <a-sphere color="green" radius="0.5" position="2 0.75 0" class="clickable">
    

    Next, you will:

    • Specify an animation,
    • Trigger the animation with a click.

    Due to Aframe’s easy-to-use animation entity, both steps can be done in quick succession.

    Add an tag on line 33, right after the tag but before the end of the .

    <a-animation begin="click" attribute="position" from="2 0.75 0" to="2.2 0.75 0" fill="both" direction="alternate" repeat="1"></a-animation>
    

    The above properties specify a number of configurations for the animation. The animation:

    • Is triggered by the click event
    • Modifies the tree’s position
    • Starts from the original position 2 0.75 0
    • Ends in 2.2 0.75 0 (moving 0.2 units to the right)
    • Animates when traveling to and from the destination
    • Alternates animation between traveling to and from the destination
    • Repeats this animation once. This means the object animates twice in total — once to the destination and once back to the original position.

    Finally, navigate to your preview, and drag from the cursor to your tree. Once the black circle rests on the tree, the tree will move to the right and back.

    Large preview

    This concludes the basics needed to build a point-and-click adventure game, in virtual reality. To view and play a more complete version of this game, see the following short scene. The mission is to open the gate and hide the tree behind the gate, by clicking on various objects in the scene.


    The mission is to open the gate and hide the tree behind the gate, by clicking on various objects in the scene.
    (Large preview)

    Next, we set up a simple nodeJS server to serve our static demo.

    Step 4: Setup NodeJS Server

    In this step, we will set up a basic, functional nodeJS server that serves your existing VR model. In the left sidebar of your editor, select package.json.

    Start by deleting lines 2-4.

    "//1": "describes your app and its dependencies",
    "//2": "https://docs.npmjs.com/files/package.json",
    "//3": "updating this file will download and update your packages", 
    

    Change the name to mirrorvr.

    {
      "name": "mirrorvr", // change me
      "version": "0.0.1",
      ...
    

    Under dependencies, add socket.io.

    "dependencies": {
      "express": "^4.16.3",
      "socketio": "^1.0.0",
    },
    

    Update the repository URL to match your current glitch’s. The example glitch project is named point-and-click-vr-game. Replace that with your glitch project’s name.

    "repository": {
      "url": "https://glitch.com/edit/#!/point-and-click-vr-game"
    },
    

    Finally, Change the "glitch" tag to "vr".

    "keywords": [
      "node",
      "vr",  // change me
      "express"
    ]
    

    Double check that your package.json now matches the following.

    {
      "name": "mirrorvr",
      "version": "0.0.1",
      "description": "Mirror virtual reality models",
      "main": "server.js",
      "scripts": {
        "start": "node server.js"
      },
      "dependencies": {
        "express": "^4.16.3",
        "socketio": "^1.0.0"
      },
      "engines": {
        "node": "8.x"
      },
      "repository": {
        "url": "https://glitch.com/edit/#!/point-and-click-vr-game"
      },
      "license": "MIT",
      "keywords": [
        "node",
        "vr",
        "express"
      ]
    }
    

    Double check that your code from the previous parts matches the following, in views/index.html.

    <!DOCTYPE html>
    <html>
      <head>
          <script src="https://aframe.io/releases/0.7.0/aframe.min.js"></script>
      </head>
      <body>
        <a-scene>
          
          <!-- blue sky -->
          <a-sky color="#a3d0ed"></a-sky>
          
          <!-- camera with wasd and panning controls -->
          <a-entity camera look-controls wasd-controls position="0 0.5 2" rotation="0 0 0">
            <a-entity cursor="fuse: true; fuseTimeout: 250"
                      position="0 0 -1"
                      geometry="primitive: ring; radiusInner: 0.02; radiusOuter: 0.03"
                      material="color: black; shader: flat"
                      scale="0.5 0.5 0.5"
                      raycaster="far: 20; interval: 1000; objects: .clickable">
                <a-animation begin="fusing" easing="ease-in" attribute="scale"
                   fill="backwards" from="1 1 1" to="0.2 0.2 0.2" dur="250"></a-animation>
            </a-entity>
          </a-entity>
            
          <!-- brown ground -->
          <a-box shadow id="ground" shadow="receive:true" color="#847452" width="10" height="0.1" depth="10"></a-box>
            
          <!-- start code here -->
          <a-sphere color="green" radius="0.5" position="2 0.75 0" class="clickable">
            <a-cylinder color="#84651e" position="0 -0.9 0" radius="0.05"></a-cylinder>
            <a-sphere color="green" radius="0.35" position="0 0.5 0"></a-sphere>
            <a-sphere color="green" radius="0.2" position="0 0.8 0"></a-sphere>
            <a-animation begin="click" attribute="position" from="2 0.75 0" to="2.2 0.75 0" fill="both" direction="alternate" repeat="1"></a-animation>
          </a-sphere>
          <!-- end code here -->
        </a-scene>
      </body>
    </html>
    

    Modify the existing server.js.

    Start by importing several NodeJS utilities.

    • Express
      This is the web framework we will use to run the server.
    • http
      This allows us to launch a daemon, listening for activity on various ports.
    • socket.io
      The sockets implementation that allows us to communicate between client-side and server-side in nearly real-time.

    While importing these utilities, we additionally initialize the ExpressJS application. Note the first two lines are already written for you.

    var express = require('express');
    var app = express();
    
    /* start new code */
    var http = require('http').Server(app);
    var io = require('socket.io')(http);
    /* end new code */
    
    // we've started you off with Express, 
    

    With the utilities loaded, the provided server next instructs the server to return index.html as the homepage. Note there is no new code written below; this is simply an explanation of the existing source code.

    // http://expressjs.com/en/starter/basic-routing.html
    app.get('/', function(request, response) {
      response.sendFile(__dirname + '/views/index.html');
    });
    

    Finally, the existing source code instructs the application to bind to and listen to a port, which is 3000 by default unless specified otherwise.

    // listen for requests :)
    var listener = app.listen(process.env.PORT, function() {
      console.log('Your app is listening on port ' + listener.address().port);
    });
    

    Once you are finished editing, Glitch automatically reloads the server. Click on “Show” in the top-left to preview your application.

    Your web application is now up and running. Next, we will send messages from the client to the server.

    Step 5: Send Information From Client To Server

    In this step, we will use the client to initialize a connection with the server. The client will additionally inform the server if it is a phone or a desktop. To start, import the soon-to-exist Javascript file in your views/index.html.

    After line 4, include a new script.

    <script src="/client.js" type="text/javascript"></script>
    

    On line 14, add camera-listener to the list of properties for the camera entity.

    <a-entity camera-listener camera look-controls...>
        ...
    </a-entity>
    

    Then, navigate to public/client.js in the left sidebar. Delete all Javascript code in this file. Then, define a utility function that checks if the client is a mobile device.

    /**
     * Check if client is on mobile
     */
    function mobilecheck() {
      var check = false;
      (function(a){if(/(android|bbd+|meego).+mobile|avantgo|bada/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)/|plucker|pocket|psp|series(4|6)0|symbian|treo|up.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
      return check;
    };
    

    Next, we will define a series of initial messages to exchange with the server side. Define a new socket.io object to represent the client’s connection to the server. Once the socket connects, log a message to the console.

    var socket = io();
    
    socket.on('connect', function() {
      console.log(' * Connection established');
    });
    

    Check if the device is mobile, and send corresponding information to the server, using the function emit.

    if (mobilecheck()) {
      socket.emit('newHost');
    } else {
      socket.emit('newMirror');
    }
    

    This concludes the client’s message sending. Now, amend the server code to receive this message and react appropriately. Open the server server.js file.

    Handle new connections, and immediately listen for the type of client. At the end of the file, add the following.

    /**
     * Handle socket interactions
     */
    
    io.on('connection', function(socket) {
    
      socket.on('newMirror', function() {
        console.log(" * Participant registered as 'mirror'")
      });
    
      socket.on('newHost', function() {
        console.log(" * Participant registered as 'host'");
      });
    });
    

    Again, preview the application by clicking on “Show” in the top left. Load that same URL on your mobile device. In your terminal, you will see the following.

    listening on *: 3000
     * Participant registered as 'host'
     * Participant registered as 'mirror'
    

    This is the first of simple message-passing, where our client sends information back to the server. Quit the running NodeJS process. For the final part of this step, we will have the client send camera information back to the server. Open public/client.js.

    At the very end of the file, include the following.

    var camera;
    if (mobilecheck()) {
      AFRAME.registerComponent('camera-listener', {
        tick: function () {
          camera = this.el.sceneEl.camera.el;
          var position = camera.getAttribute('position');
          var rotation = camera.getAttribute('rotation');
          socket.emit('onMove', {
            "position": position,
            "rotation": rotation
          });
        }
      });
    }
    

    Save and close. Open your server file server.js to listen for this onMove event.

    Add the following, in the newHost block of your socket code.

    socket.on('newHost', function() {
        console.log(" * Participant registered as 'host'");
        /* start new code */
        socket.on('onMove', function(data) {
          console.log(data);
        });
        /* end new code */
      });
    

    Once again, load the preview on your desktop and on your mobile device. Once a mobile client is connected, the server will immediately begin logging camera position and rotation information, sent from the client to the server. Next, you will implement the reverse, where you send information from the server back to the client.

    Step 6: Send Information From Server To Client

    In this step, you will send a host’s camera information to all mirrors. Open your main server file, server.js.

    Change the onMove event handler to the following:

    socket.on('onMove', function(data) {
      console.log(data);  // delete me
      socket.broadcast.emit('move', data)
    });
    

    The broadcast modifier ensures that the server sends this information to all clients connected to the socket, except for the original sender. Once this information is sent to a client, you then need to set the mirror’s camera accordingly. Open the client script, public/client.js.

    Here, check if the client is a desktop. If so, receive the move data and log accordingly.

    if (!mobilecheck()) {
      socket.on('move', function(data) {
        console.log(data);
      });
    }
    

    Load the preview on your desktop and on your mobile device. In your desktop browser, open the developer console. Then, load the app on your mobile phone. As soon as the mobile phone loads the app, the developer console on your desktop should light up with camera position and rotation.

    Open the client script once more, at public/client.js. We finally adjust the client camera depending on the information sent.

    Amend the event handler above for the move event.

    socket.on('move', function(data) {
      /* start new code */
      camera.setAttribute('rotation', data["rotation"]);
      camera.setAttribute('position', data["position"]);
      /* end new code */
    });
    

    Load the app on your desktop and your phone. Every movement of your phone is reflected in the corresponding mirror on your desktop! This concludes the mirror portion of your application. As a desktop user, you can now preview what your mobile user sees. The concepts introduced in this section will be crucial for further development of this game, as we transform a single-player to a multiplayer game.

    Conclusion

    In this tutorial, we programmed three-dimensional objects and added simple interactions to these objects. Additionally, you built a simple message passing system between clients and servers, to effect a desktop preview of what your mobile users see.

    These concepts extend beyond even webVR, as the notion of a geometry and material extend to SceneKit on iOS (which is related to ARKit), Three.js (the backbone for Aframe), and other three-dimensional libraries. These simple building blocks put together allow us ample flexibility in creating a fully-fledged point-and-click adventure game. More importantly, they allow us to create any game with a click-based interface.

    Here are several resources and examples to further explore:

    • MirrorVR
      A fully-fledged implementation of the live preview built above. With just a single Javascript link, add a live preview of any virtual reality model on mobile to a desktop.
    • Bit by Bit
      A gallery of kids’ drawings and each drawing’s corresponding virtual reality model.
    • Aframe
      Examples, developer documentation, and more resources for virtual reality development.
    • Google Cardboard Experiences
      Experiences for the classroom with custom tools for educators.

    Next time, we will build a complete game, using web sockets to facilitate real-time communication between players in a virtual reality game. Feel free to share your own models in the comments below.

    Smashing Editorial(rb, ra, yk, il)
Categories: Others Tags: