Archiwum kategorii: CSS

Animate Calligraphy with SVG

Post pobrano z: Animate Calligraphy with SVG

From time to time at Stackoverflow, the question pops up whether there is an equivalent to the stroke-dashoffset technique for animating the SVG stroke that works for the fill attribute. But upon closer inspection, what the questions are really trying to ask is something like this:

I have something that is sort of a line, but because it has varying brush widths, in SVG it is defined as the fill of a path.

How can this „brush” be animated?

In short: How do you animate calligraphy?

A mask path covers the calligraphic brush

The basic technique for this is relatively simple: draw a second (smooth) path on top of the calligraphy so that it follows the brush line and then choose the stroke width in such a way that it covers the calligraphy everywhere.

This path on top will be used as a mask for the one beneath it. Apply the stroke-dashoffset animation technique to the mask path. The result will look as if the lower path is being „written” directly on the screen in real-time.

The is a case for a mask, not a clip-path — that would not work. Clip-paths always reference the fill area of a path, but ignore the stroke.

The easiest variant is to set stroke: white for the path in the mask. Then everything outside the area painted white is hidden, and anything inside is shown without alteration.

See the Pen Writing calligraphy: basic example by ccprog (@ccprog) on CodePen.

So far, so simple. Things get tricky, however, when the calligraphic lines overlap. This is what happens in a naive implementation:

See the Pen Writing calligraphy: faulty intersection by ccprog (@ccprog) on CodePen.

At the intersection point, the mask reveals part of the crossing brush. Therefore, the calligraphy has to be cut into non-overlapping pieces. Stack them in drawing order and define separate mask paths for each one.

The cut on the mask path and the calligraphic brush must match

The most tricky part is to maintain the impression that the drawing is a single continuous stroke. If you cut a smooth path, ends will fit together as long as both path tangents have the same direction at their common point. The stroke ends are perpendicular to that, and it is essential that the cut in the calligraphic line aligns exactly. Take care all paths have consecutive directions. Animate them one after the other.

While many line animations can get by with rough math on the length for stroke-dasharray, this scenario requires accurate measurements (although small roundings shouldn’t hurt). As a reminder, you can get them in the DevTools console with:

document.querySelector('#mask1 path').getTotalLength()

See the Pen Writing calligraphy: divide up intersections by ccprog (@ccprog) on CodePen.

The „one after the other” part is slightly awkward to write in CSS. The best pattern is probably to give all partial animations the same start time and total duration, then set intermediate keyframes between the stroke-dashoffset changes.

Something like this:

@keyframes brush1 {
  0% { stroke-dashoffset: 160; } /* leave static */
  12% { stroke-dashoffset: 160; } /* start of first brush */
  44% { stroke-dashoffset: 0; }   /* end of first brush equals start of second */
  100% { stroke-dashoffset: 0; }   /* leave static */
}

@keyframes brush2 {
  0% { stroke-dashoffset: 210; } /* leave static */
  44% { stroke-dashoffset: 210; } /* start of second brush equals end of first */
  86% { stroke-dashoffset: 0; }   /* end of second brush */
  100% { stroke-dashoffset: 0; }   /* leave static */
}

Further down, you’ll see how a SMIL animation enables a more fluent and expressive way to define timing. Keeping with CSS, computations done with Sass might be pretty helpful since it can handle some math.

The mask path (left) and its application (right)

A comparable problem appears if the curve radius of the mask path gets smaller than the stroke width. While the animation runs through that curve, it may happen that an intermediate state looks seriously crooked.

The solution is to move the mask path out of the calligraphic curve. You only need to take care its inner edge still covers the brush.

You can even cut the mask path and misalign the ends, as long as the cutting edges fit together.

The radius stays large enough

See the Pen Writing calligraphy: divide up intersections by ccprog (@ccprog) on CodePen.

And, thus, you can even draw something complex, like the Arabic calligraphy in this example:

See the Pen Tughra Mahmud II – text animation by ccprog (@ccprog) on CodePen.

The original design, the Tughra of Osmanic Sultan Mahmud II., is by an unknown 19th-century calligrapher. The vectorized version was done by Wikipedia illustrator Baba66. The animation is my attempt to visualize the position of the Arabic letters inside the drawing. It builds upon an earlier version by Baba66. Creative Commons Attribution-Share Alike 2.5.

The following code snippet shows the advanced method used to run the animations in order and in a repeatable fashion.

mask path {
  fill: none;
  stroke: white;
  stroke-width: 16;
}

.brush {
  fill: #0d33f2;
}
<mask id="mask1" maskUnits="userSpaceOnUse">
  <path stroke-dasharray="160 160" stroke-dashoffset="160" d="...">
    <!-- animation begins after document starts and repeats with a click
         on the "repeat" button -->
    <animate id="animate1" attributeName="stroke-dashoffset"
             from="160" to="0" begin="1s;repeat.click" dur="1.6s" />
  </path>
</mask>
<mask id="mask2" maskUnits="userSpaceOnUse">
  <path stroke-dasharray="350 350" stroke-dashoffset="350" d="...">
    <!-- animation begins at the end of the previous one -->
    <animate id="animate2" attributeName="stroke-dashoffset"
             from="350" to="0" begin="animate1.end" dur="3.5s" />
  </path>
</mask>
<!-- more masks... -->
<mask id="mask15" maskUnits="userSpaceOnUse">
  <path stroke-dasharray="230 230" stroke-dashoffset="230" d="...">
    <!-- insert an artificial pause between the animations, as if the
         brush had been lifted -->
    <animate id="animate15" attributeName="stroke-dashoffset"
             from="230" to="0" begin="animate14.end+0.5s" dur="2.3s" />
  </path>
</mask>

<g class="brush">
  <path id="brush1" d="...">
    <!-- The mask is only applied  after document starts/repeats and until
         the animation has run. This makes sure the brushes are visible in
         renderers that do not support SMIL -->
    <set attributeName="mask" to="url(#mask1)"
         begin="0s;repeat.click" end="animate1.end;indefinite" />
  </path>
  <path id="brush2" d="...">
    <set attributeName="mask" to="url(#mask2)"
         begin="0s;repeat.click" end="animate2.end;indefinite" />
  </path>
  <!-- more paths... -->
  <path id="brush15" d="...">
    <set attributeName="mask" to="url(#mask2)"
         begin="0s;repeat.click" end="animate15.end;indefinite" />
  </path>
</g>

In contrast to the other examples we’ve look at, this animation uses SMIL, which means it will not work in Internet Explorer and Edge.


This article is published in German over at Browser…​unplugged.

The post Animate Calligraphy with SVG appeared first on CSS-Tricks.

Don’t Use The Placeholder Attribute

Post pobrano z: Don’t Use The Placeholder Attribute

Eric Bailey takes a hardline position on <input placeholder>.

You might be thinking, as I did: yeah, yeah I know the pitfalls. I’m capable of using placeholder responsibly. But when you look at all the negatives together:

  • Can’t be automatically translated;
  • Is oftentimes used in place of a label, locking out assistive technology;
  • Can hide important information when content is entered;
  • Can be too light-colored to be legible;
  • Has limited styling options;
  • May look like pre-filled information and be skipped over.

…and the fact that there are advantages to just moving whatever helper text you would want in there anyway outside the input to real markup…I’m fairly well convinced.

Direct Link to ArticlePermalink

The post Don’t Use The Placeholder Attribute appeared first on CSS-Tricks.

Balancing Time

Post pobrano z: Balancing Time

I first wrote this post four years ago. I put it on a blog that no longer exists. Funnily enough, I still refer to it myself, so I figured it might be best served in a place where other people can see it. I’ve made only a few minor tweaks to the original content. A lot about how I work has changed, but most of these pieces have not.


I work on many personal projects concurrently. I love doing this, as it keeps me in a constant mode of creation. At the same time, it can become a delicate balancing act. In order to keep everything moving forward, I have set up some guidelines for myself and I’m going to share them with you in this post. However, it’s more important to understand what works best for you and consider the sage words of Thich Nhat Hahn:

“Don’t follow someone else’s map.”

What works for me might not work for you. However, hearing about how other people set up their workflow might help you reconsider your own, so hopefully this is a useful mental exercise.

Organize

Every three months or so, I compile a list of all my projects. The projects that have time parameters go first in line (but do not always come first if my passion lies elsewhere). For each project, I break down the tasks I need to perform in order to get them done. I try to make each one as small and actionable as possible.

grid of X drawn out on a moleskine sketchbook

I actually write out the same list multiple ways — some digital, some paper. I find the repetition helps me reinforce and recall what my priorities are. I also find large gridded Moleskine sketchbooks useful for organizing myself. It’s important to me that they’re large enough to have enough space to write notes, draw terrible sketches, or generally spread out.

Unless they must be done in order, I often place the easiest and quickest tasks first so I can get them out of the way. I love the feeling of checking things off my list because it serves as positive re-enforcement. I make a schedule of due dates for items that have no due dates; having a specific deadline helps me push forward.

Only stop when you know what happens next

This a really simple and useful trick. If you stop when your plan is veering or meandering, then chances are that project will never see the light of day again. If you can help it, try to only stop working when you know what the next actionable item is. You will arrive back at the project with brighter eyes than before.

Of course, this one doesn’t work when you’ve smacked your head on a problem forever and ever. Sometimes you need a break and just leaving it for a little while or coming back to it the next day will help you solve it. Generally speaking though, you still still know what comes next even before you come to a solution. It’s more the feeling of malaise that I’m warning against. Stop while you still have a plan.

Even backwards work is forward progress

fingers on a keyboard
Photo by John Schnobrich, via Unsplash

Allow yourself to make mistakes. Seriously, make them. I used to tell my students that it takes two bad projects to make a good one. Never compare yourself to what others show you — their final output is often the result of many failed attempts. Make a lot and then edit down. Try not to get discouraged if you spend a day or two messing up. You’ll likely learn more from those mistakes than if you had done everything perfectly. Forge ahead.

Push outside of your comfort zone, but slowly

Work on a few things that you know and understand, and a few things you don’t. We should foster personal growth in our projects, but without some semblance of comfort, it’s easy to get discouraged. Let your projects push the limits of your boundaries, but don’t go overboard. Give yourself a foundation to spring off before floating into space.

Figure out your “studio commandments”

What do you need to work? Do you need music? Do you need water? Do you need isolation? Do you work better in a cafe? Maybe you need a certain monitor. Perhaps you concentrate better if you have scheduled breaks? Do you work better if you have a snack in your bag in case you get hungry? Study yourself. Write down 10 things you need in order to be productive and try to sort them out before you get going so that you give yourself fewer excuses to stop making progress. I wrote another post about this on SuperYesMore, posted here.

Make the time

I usually break up my time so that I have certain nights, like appointments, where I work on each project. There are times, however, where you should keep on going and not worry about other outstanding projects. Sometimes working 15 or more continuous hours will gain you traction in a way that you can’t if you break it up over several weeks. However…

Break your own rules

Do you have a consistent schedule and you can’t possibly do X because of Y? Double and triple check that you aren’t setting up guidelines for yourself that give you excuses ti not get things done. Something that worked last week might not work this week, so take the time to re-evaluate your own rules. In other words: be adaptive.

Do things for other people

Make one of your projects something that benefits others. This guideline might sound strange, but if you continuously work on things just for yourself, work starts to feel a little dreary. I’m not talking about your job, either. It needn’t be overkill. Even donating a few hours here or there to people who need it will help you think more broadly about the world around you.

Bonus: If you build a community and reach the point where you’re ready to launch that donated project, you might just have other people around you who to share the excitement of a job well done.

Research is productive time

Reading about what you find interesting or doing a little bit of digging about your interests can save you time in process. Find heroes in the field in which you are working and follow them. I have so many.

Poke around at other things. Reverse engineer something. I have a giant private collection on CodePen called „To reverse engineer.” When I’m bored at an airport, or sitting on hold, or have an hour to kill, I’m usually poking around in that folder: breaking things, changing things, finding the boundaries of how everything works.

Read a book, read some docs, watch some talks, do a workshop, watch some anime, go to a museum, listen to some music with your eyes closed, play a video game. Need a starting point? CodePen Challenges are good for that. There are all kinds of things that can feed your work. All of this spent time — while it won’t make your GitHub contribution graph greener — is important for development.

Make it fun

photo of a mimosa and a woman's torso
Photo by Hanny Naibaho, via Unsplash

Personal projects are fun because you want to do them, right? Enjoy yourself. I really like mimosas on the weekends so I enjoy one by my window every Saturday morning while writing code. It’s a very simple trick. It’s not drudgery; it’s pure enjoyment. I turn down other plans to do this because I love it. Find ways to make the work a reward in and of itself. Maybe you only get to listen to that one album you like while you work on a side project. Maybe you have a nice fuzzy blanket that you get to wrap yourself in while you read that programming book. Maybe you get to go to the park after you get that last component in. You know the difference between something that genuinely excites you to work or not — use this to your advantage.

I also imagine how I might feel when something is done. This particularly works when the only thing keeping me from finishing something is a bit of drudgery. I imagine it’s done and feel the little dopamine rush from accomplishing something and not letting it sit. Then I chase that feeling.

The post Balancing Time appeared first on CSS-Tricks.

Advanced Document Conversions with Filestack

Post pobrano z: Advanced Document Conversions with Filestack

You might know Filestack from being an incredible service to add file uploading, storage, and management to your own web apps.

There is another thing Filestack can do for you: convert documents into different formats.

For one thing, it can manipulate documents. Take images. Perhaps you would like to offer some image manipulation for your users uploaded images, like cropping and rotation. That’s a common feature for apps that offer avatar uploading. With Filestack, you got it.

It’s great to be able to have that kind of functionality without having to build it yourself. You almost surely aren’t in the avatar cropping business, you’re in your own unique business that just happens to have users with avatars.

I’ve said it before:

Being smart is using a service like Filestack to handle files while you focus on what your app really does.

But let’s up the ante a little bit here. What if you need to get an entirely different document format out of another document? How about the hardest document format at all, feared by web developers everywhere, the Microsoft Word document format! 😱

Filestack has you covered! Say you have an invoice document created in Word and you need it as a PDF. But a perfect PDF, not some hack job conversion. And you need to do it programmatically. That’s tricky stuff, and Filestack has solved it.

Of course, it isn’t just Microsoft Word document formats, they have a whole matrix of in’s and out’s.

See full size

Just about any file format conversion you need, they got. Personally, I’m impressed by the idea that conversion between PDF and SVG maintains the vectors in both directions.

Document security is a prime concern of course and is handled through policy and signature validation.

See their security docs for all things security related.

Need a way to show users files like PDFs? Browsers can view PDFs, but only as a whole, not embedded into regular web pages. Not a problem with the document viewer!

If you think Filestack might solve some problems for your app, like transforming files, check out their getting started guide.

The post Advanced Document Conversions with Filestack appeared first on CSS-Tricks.

Centering: The Newest Coolest Way vs. The Oldest Coolest Way

Post pobrano z: Centering: The Newest Coolest Way vs. The Oldest Coolest Way

This isn’t a comprehensive guide to centering things. We have that!

This is just a little observation about old and new. One of the trickier things related to centering in CSS is when you need to center both vertically and horizontally and you don’t know the width or height of what you are centering. Vertical centering being the extra tricky of the two.

Believe it or not, there was a way to do that even in IE 8. Czytaj dalej Centering: The Newest Coolest Way vs. The Oldest Coolest Way

Don’t just copy the @font-face out of Google Fonts URLs

Post pobrano z: Don’t just copy the @font-face out of Google Fonts URLs

I don’t think this is an epidemic or anything, but I’ve seen it done a few times and even advocated for. This is what I mean…

You go to Google Fonts and pick a font like Open Sans, and it gives you either a <link> or an @import with a URL there in which to ready this font for usage on your site.

You can take a peek in there and see what it returns…

It’s just some @font-face declarations, of course!

Now your performance-minded brain kicks off. Wait. So, I make one HTTP request for this stylesheet, and then it makes more HTTP requests for those woff2 files it’s linking up. Screw the middle man here, why not just copy those @font-face blocks right out of here and use them.

You can! But!

The issue is that Google does fancy Google things here and the contents of that original stylesheet changes based on the browser requesting it. That screenshot above is Chrome 66. Here’s Firefox 20 on Windows 7:

It’s different! It’s only got woff, not woff2. If we open that URL in IE 8, we’d get an @font-face block that includes the eot format!

The point is, what that URL gives is very specific to what the current browser needs. That’s a pretty cool thing to abstract away and not worry about. Should new browsers have new formats and new CSS syntax needed, that’ll just come along for the ride.

Not that Google Fonts is perfect with this stuff. For example, by not controlling your own @font-face blocks, you can’t take advantage of font-display, which is a shame. Maybe we’ll get that someday, or maybe it’s worth self-hosting your Google Fonts, which is another whole thing we’ll get into someday.

The post Don’t just copy the @font-face out of Google Fonts URLs appeared first on CSS-Tricks.

The Four Big Ways Jetpack Helps with Image Performance

Post pobrano z: The Four Big Ways Jetpack Helps with Image Performance

We’ve been working with Jetpack around here as a sponsor. It’s a great match because as someone with a bunch of self-hosted WordPress sites, Jetpack is one of those no-brainer plugins for me. Jetpack can do a ton of good things for any site in a variety of very different ways. Here’s one way to think about it: it brings the power of WordPress’ own massive servers to you.

For now, let’s just focus on one angle of what Jetpack can do for you: image performance. Jetpack does a ton for you in this regard, solving some non-trivial performance upgrades. Let’s take a look at what I see as the four big boosts you get from Jetpack on your images.

1) WordPress does responsive images for you

OK, I cheated with the first one because you don’t actually need Jetpack to benefit from this. But it’s an important and foundational concept for fast images. Just by using WordPress, you get basic responsive images for free.

If you already know what I’m talking about, here’s an example of the output you’ll see in the DOM of a published WordPress post with an image in it uploaded via the Media Uploader:

It’s wonderful to get this for free, as writing out responsive images syntax by hand is quite cumbersome.

If you are new to the idea of responsive images, the big idea is this: rather than a single image going to any browser visiting your website, you have multiple images in different sizes and the most-correct one is delivered. Imagine instead of a mobile phone downloading a 1600 pixel wide image (way bigger than it needs), it only downloads a 320-pixel wide image, saving a ton of downloading time.

We’ve written lots about responsive images over the years.

2) You get a CDN

Read a bit about web performance and you’ll be unanimously told: „use a CDN.” A CDN is a Content Delivery Network, essentially web servers designed specifically to make serving assets like images super fast. They call it a network because it isn’t just one server, it’s many servers physically located all over the world so that when your website is requested from different locations all around the world, the files being sent back come from geographically closer locations (faster!). Not to mention it does other clever things like not requiring cookies for each web request like your own server probably does.

Literally, flip a switch in Jetpack and you’ll be using an image CDN:

It’s called Photon.

Site speed is impacted by many factors and one of them is content delivery. Using what is referred to as a content delivery network (or CDN) helps by:

  • Delivering your content from high-speed and dedicated data centers.
  • More files can be downloaded simultaneously by the browser.
  • Distributed data centers (ie in different geographic locations) improve download speeds and provide redundancy.
  • By distributing load and save bandwidth you reduce your existing hosting costs (or keep them in check).

3) You get optimization

Una Kravets calls image optimization an easy performance win for designers. It’s an easy thing to see. Try taking a screenshot of something, exporting something from Photoshop, or grabbing some stock photography. Then drop it onto a tool like ImageOptim and watch the bytes fall away as it optimizes it. Massive savings.

But wouldn’t it be nice if it wasn’t on you to manually optimize all your images before using them? Computers are supposed to help us with menial tasks, right?! When you flip on the CDN feature of Jetpack, your images are now hosted on Photon, and you can see in the Photon docs how it handles things like resizing and quality for you.

4) You get lazy loading

Lazy loading is the idea that you don’t load anything at all unless you need it. In the case of images, don’t download the image unless it’s visible on the page. As in, don’t download an image that is three quarter down an article that a user might never scroll down to, but if they do, then download it.

You know what they say, the fastest web request is one that is never made. Jeremy Wagner, for Google, says:

When we lazy load images and video, we reduce initial page load time, initial page weight, and system resource usage, all of which have positive impacts on performance.

This is another flip-a-switch feature that works on any theme. Turn it on, you got lazy loading.

All Together Now

  1. You get responsive images with WordPress, which by itself can be a major performance win.
  2. With Jetpack, those responsive images are CDN-hosted, providing a speed boost and great caching for the images that are downloaded.
  3. Just because you’re using responsive images and a CDN doesn’t automatically mean those images are optimized, but they are on Photon.
  4. Last, nothing is downloaded at all unless the images are in view (lazy loading), which is the most efficient thing you can do.

Pretty compelling.

The post The Four Big Ways Jetpack Helps with Image Performance appeared first on CSS-Tricks.

Building a RSS Viewer With Vue: Part 2

Post pobrano z: Building a RSS Viewer With Vue: Part 2

Welcome to Part 2 of this mini-series on building a RSS viewer with Vue. In the last post, I walked through how I built my demo using Vue.js and Vuetify on the front end and Webtask on the back end. When I built that initial version, I knew it was exactly thatmdash;an „initial” version. I took some time to work on a few updates, and while I won’t dare call this a „perfect” version, I do think I’ve made some improvements and I’d like to share them with you.

Article Series:

  1. Setup and first iteration
  2. Refinements and final version (This Post)

Before I get started, here are links to the completed demo and source code.

Feel free to fork, file PRs, and report bugs to your heart’s content!

The Plan

When I shared the initial version in Part 1, I outlined some ideas to improve the RSS reader, including:

  • Moving to Vuex.
  • Starting to switch to components in the layout. (Well, I was already using Vuetify components, but I meant custom components for my application.)
  • Using IndexedDB to store feed items for quicker access and offline support.

That was the plan, and like most plans, I wasn’t necessarily able to hit everything in this update (and I’ll explain why at the end). But hopefully you’ll see the improvements as a general „moving in the right direction” for the application. With that out of the way, let’s get started!

Implementing Vuex

I’ll start off discussing the biggest change to the application, the addition of Vuex. As I said in the previous post, Vuex describes itself as a „state management pattern + library” on their „What is Vuex” page. No offense to their documentation, but I had a difficult time wrapping my head around exactly what this meant, from a practical sense.

After having using it in a few small projects now, I’m coming to appreciate what it provides. To me, the core benefit is providing a central interface to your data. If I’ve got a basic Vue app working with an array of values, I may have multiple different methods that modify it. What happens when I begin to have certain rules that must be applied before the data changes? As a simple example, imagine an array of RSS feeds. Before I add a new one, I want to ensure it doesn’t already exist in the list. If I have one method that adds to the feed list, that isn’t a problem, but if I have more, it may become cumbersome to keep that logic in sync across the different methods. I could simply build a utility to do this, but what happens when I have other components in play as well?

While it is absolutely not a one-to-one comparison, I feel like Vuex reminds me of how Providers or Services work in Angular. If I ever want to do work with any data, I’ll ensure I use a central provider to handle all access to that data. That’s how I look at Vuex.

So the big change in this application was to migrate all the data related items to a store. I began by adding the library to my HTML:

<script src="https://unpkg.com/vuex"></script>

Woot! Half-way done! (OK maybe not.)

I then created an instance of my store in my JavaScript file:

const feedStore = new Vuex.Store({
  // lots of stuff here
});

and included it in my Vue app:

let app = new Vue({ 
  el: '#app',
  store:feedStore,
  // lots of stuff here too...
});

Now comes the interesting part. Any time my Vue application needs data, which primarily consists of the list of feeds and the items from those feeds, it’s going to ask the store for them. So, for example, my feeds value is now computed:

feeds() {
  return feedStore.state.feeds;
},

This is now defined in the state portion of my store:

state: {
  allItems: [],
  feeds: [],
  selectedFeed: null
},

Notice that feeds defaults to an empty array. I had previously used the created event of my Vue app to read in the data from localStorage. Now, I ask the store to do that:

created() {
  feedStore.dispatch('restoreFeeds');
},

Back in the store, the logic is pretty much the same:

restoreFeeds(context) {
  let feedsRaw = window.localStorage.getItem('feeds');
  if(feedsRaw) {
    try {
    let feeds = JSON.parse(feedsRaw);
    context.state.feeds = feeds;
    context.state.feeds.forEach(f => {
      context.dispatch('loadFeed', f);
    });
    } catch(e) {
      console.error('Error restoring feed json'+e);
      // bad json or other issue, nuke it
      window.localStorage.removeItem('feeds');
    }
  }
},

I say „pretty much the same” except now I’m doing a bit of error-checking on the value read in from localStorage. But here’s the crucial bit. I already said I failed in terms of switching to IndexedDB, but in theory, I could build a third version of this application with an updated store and my Vue app won’t know the difference. And that’s where I started to get really excited. The more I worked, the more „dumb” my Vue app became and the less tied it was to any particular implementation of storage. Let’s look at the complete Vue app now:

let app = new Vue({ 
  el: '#app',
  store:feedStore,
  data() {
    return {
      drawer:true,
      addFeedDialog:false,
      addURL:'',
      urlError:false,
      urlRules:[],
      selectedFeed:null
    }
  },
  computed: {
    showIntro() {
      return this.feeds.length == 0;
    },
    feeds() {
      return feedStore.state.feeds;
    },
    items() {
      return feedStore.getters.items;
    }
  },
  created() {
    feedStore.dispatch('restoreFeeds');
  },
  methods:{
    addFeed() {
      this.addFeedDialog = true;
    },
    allFeeds() {
            feedStore.dispatch('filterFeed', null);
    },
    addFeedAction() {
      this.urlError = false;
      this.urlRules = [];

      feedStore.dispatch('addFeed', {url:this.addURL})
      .then(res => {
        this.addURL = '';
        this.addFeedDialog = false;
      })
      .catch(e =>{
        console.log('err to add', e);
        this.urlError = true;
        this.urlRules = ["URL already exists."];                                
      });
    },
    deleteFeed(feed) {
      feedStore.dispatch('deleteFeed', feed);
    },
    filterFeed(feed) {
      feedStore.dispatch('filterFeed', feed);
    }
  }
})

What you’ll notice is that pretty much all of the actual logic is now gone and all I’m really doing here is UI stuff. Open a modal here, add an error there, and so forth.

You can view the complete store here, although I apologize for lumping everything together in one file.

Adding a Component

One of the other changes I mentioned was beginning to „component-ize” the view layer. I ended up only making one component, feed-item. This reduced the total number of lines in the HTML a bit:

<v-flex xs12 v-for="item in items" :key="item.link">
  <feed-item :title="item.title" :content="item.content" :link="item.link" :feedtitle="item.feedTitle" :color="item.feedColor" :posted="item.pubDate"></feed-item>
</v-flex>

It isn’t a huge change by any means, but it did make it bit easier for me when I started working on the feed display. As I’m not using a fancy builder yet, I defined my component straight in JavaScript like so:

Vue.component('feed-item', {
  props:[
    'color','title','content','link','feedtitle', 'posted'
  ],
  template: `
  <v-card :color="color">
    <v-card-title primary-title>
      <div class="headline">{Building a RSS Viewer With Vue: Part 2} ({{posted | dtFormat}})</div>
    </v-card-title>
    <v-card-text>
      {{content | maxText }}
    </v-card-text>
    <v-card-actions>
      <v-btn flat target="_new" :href="link">Read on {CSS-Tricks}</v-btn>
    </v-card-actions>
  </v-card>        
  `
});

I’m not doing anything at all fancy in heremdash;there’s no dynamic logic or events or anything like that, but I could certainly add that later where it makes sense. I did finally get around to adding the date and time of posting. If you’re curious about how I built the formatter used for it, read my article Build A i18n Filter Using Vue.js & Native Web Specs.”

The Power of Delete!

Oh, and I finally added a way to delete feeds:

Trash can icon, FTW!

This just fires off a method on the Vue object that, in turn, fires off a call to the store that takes care of removing the feed and items from the UI and then persisting it. A small thing, but, wow, did I wish I had that in the first version when testing. And here is a final shot of everything:

The awesome app in all it’s awesomeness

Next Steps… and What Happened to IndexedDB?

As I said in the beginning, this version is still not perfect but I definitely feel better about it. I highly encourage you to share tips, suggestions, and bug reports in the comments below or on the GitHub repo.

So what happened to IndexedDB support? The issue I ran into was how to properly initialize the database. Vuex stores don’t have a concept of a created process. I could have done something like this:

// dummy code for getting feeds
dispatch('getDB')
.then(() =>
  // do stuff
);

Where the getDB action returns a promise and handles doing a one-time IndexedDB opening and storing the value in the state. I may give this a shot later, and again, what I love about Vuex is that I know I can safely do that without interfering with the rest of the application.


Article Series:

  1. Setup and first iteration
  2. Refinements and final version (This Post)

The post Building a RSS Viewer With Vue: Part 2 appeared first on CSS-Tricks.

Creating your own meme generator

Post pobrano z: Creating your own meme generator

Almost every time a new meme pops up in my Twitter feed, I think of a witty version to create. I’m not alone in this. Memes are often a way to acknowledge a shared experience or idea. In a variation of the „Is this a pigeon” meme that has been making the rounds online, a designer Daryl Ginn joked about the elementary nature of most applications that say they use artificial intelligence.

pic.twitter.com/nAHki0YFyV

— Daryl Ginn (@darylginn) May 16, 2018

Several people replied to his tweet saying something along the lines of „replace this with this.” Daryl’s version got them thinking about other possible variations. Platforms like imgFlip exist to make meme generations fast and easy. However, there is only so much customization they can allow. For many memes, creating new versions can only be done by people with Photoshop knowledge. But it doesn’t have to be so! For some memes that require more than Impact for the font text on an image, a meme generator can be created using the HTML Canvas API. In this tutorial, we’re going to make a generator for the #saltbae meme.

But first…

Let’s look at some fun interactive meme examples!

The website pablo.life allows you to create your own Kanye West TLOP album cover by changing the text and image.

This is one of my favorites:

The digital agency R/GA created the Straight Outta Somewhere campaign where users „show the world where they’re from by uploading their own photo and filling in the blank after 'Straight Outta ____.'” Users can download and share the meme.

Developer Isaac Hepworth created the Trump Executive Order Generator.

Spotify collaborated with Migos to create a range of downloadable Valentine’s Day cards that can be customized by changing names.

Let’s build our own meme generator!

Now, the tutorial. In a popular version of the #saltbae meme, instead of salt, Salt Bae (whose name is Nusret Gökçe) sprinkles something other than salt.

Loading an image

The first thing we have to do is load the original image onto the canvas. You can load an image one of two ways: from a URL or from one that exists in the DOM using the <img> tag but is hidden.

Here’s how we do it with a hidden image tag:

<canvas id="canvas" width="1024" height="1024">
  Canvas requires a browser that supports HTML5.
</canvas>
<img crossOrigin="Anonymous" id="salt-bae" src="http://res.cloudinary.com/dlwnmz6lr/image/upload/v1520011253/170203-salt-bae-mn-1530_060e5898cdcf7b58f97126d3cfbfdf71.nbcnews-ux-2880-1000_kllh1d.jpg"/>

I’m hosting the image on Cloudinary and added the crossOrigin attribute so we don’t run into any CORS issues.

function drawImage(text) {
  const canvas = document.getElementById('canvas');
  const ctx = canvas.getContext('2d');
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  const img = document.getElementById('salt-bae');  
  ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
}

window.onload = function() {
  drawImage();
}

We’re using the canvas drawImage function to draw the image to the canvas. It can be used to draw videos or parts of an image as well. The method provides different ways to do this. We’re drawing the image by indicating the position and the width and height of the image.

ctx.drawImage(img, x, y, width, height);

Alternatively, we could load the image from a URL:

function loadAndDrawImage(src) {
  // Create an image object. (Not part of the dom)
  const image = new Image();
  
  // After the image has loaded, draw it to the canvas
  image.onload = () => { 
    // draw image 
  };

  // Then set the source of the image that we want to load
  image.src = src;
}

Now we load in an image to replace the sprinkles Salt Bae is throwing. First, we load the image using one of the techniques I mentioned earlier, then we draw it to the screen like we did with the Salt Bae base image.

function getRandomInt(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min)) + min; //The maximum is exclusive and the minimum is inclusive
}

function drawBackgroundImage(canvas, ctx) {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  const img = document.getElementById('salt-bae');  
  ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
}

function getRandomImageSize(min, max, width, height) {
  const ratio = width / height;  // Used for aspect ratio
  width = getRandomInt(min, max);
  height = width / ratio;  
  return { width, height };
}

function drawSalt(src, canvas, ctx) {
  // Create an image object. (Not part of the dom)
  const image = new Image();
  image.src = src;
  
  // After the image has loaded, draw it to the canvas
   image.onload = function() {
    for (let i = 0; i < 8; i++) {
      const randomX = getRandomInt(10, canvas.width/2);
      const randomY = getRandomInt(canvas.height-300, canvas.height);
      const dimensions = getRandomImageSize(20, 100, image.width, image.height);
      ctx.drawImage(image, randomX, randomY, dimensions.width, dimensions.height);
    }
  }
  return image;
}

onload = function() {
  const canvas = document.getElementById('canvas');
  const ctx = canvas.getContext('2d');
  drawBackgroundImage(canvas, ctx);
  const saltImage = drawSalt('http://res.cloudinary.com/dlwnmz6lr/image/upload/v1526005050/chadwick-boseman-inspired-workout-program-wide_phczey.webp', canvas, ctx);
};

Now we can let users sprinkle something other than sprinkles.

Uploading an image

We’re going to add a button that triggers an image upload and includes an event listener to listen for a change.

<input type="file" class="upload-image">`
function updateImage(file, img){
  img.src = URL.createObjectURL(file);
}

onload = function() {
  const canvas = document.getElementById('canvas');
  const ctx = canvas.getContext('2d');
  drawBackgroundImage(canvas, ctx);
  const saltImage = drawSalt('http://res.cloudinary.com/dlwnmz6lr/image/upload/v1526005050/chadwick-boseman-inspired-workout-program-wide_phczey.webp', canvas, ctx);
  const input = document.querySelector("input[type='file']");
  /*
   * Add event listener to the input to listen for changes to its selected
   * value, i.e when files are selected 
   */
  input.addEventListener('change', function() {
    drawBackgroundImage(canvas, ctx); // clear canvas and re-draw
    updateImage(this.files[0], saltImage);
  });
};

URL.createObjectURL() creates a DOMString containing a URL representing the object given in the parameter which, in this case, is the uploaded file.

We can even up the game a little bit, like providing some default options. I’ve added a few emojis you can play around with as a starting point.

Downloading the final image

Once the new meme has been generated, we want users to be able to download and share it. The typical way of doing this is by opening the canvas in a new tab using the toDataURL method but the user would have to right click to save the image from that tab and that’s not very convenient.

So, instead, we can take advantage of the download attribute added to links in HTML5. We create a link that, on click, sets the download attribute to the result of canvas.toDataURL. The toDataURL() method „returns a data URI containing a representation of the image in the format specified.”

function addLink() {
  var link = document.createElement('a');
  link.innerHTML = 'Download!';
  link.addEventListener('click', function(e) {
    link.href = canvas.toDataURL();
    link.download = "salt-bae.png";
  }, false);
  link.className = "instruction";
  document.querySelectorAll('section')[1].appendChild(link);
}

Well that’s it! Our meme generator is done.

Some cool links

  • Darius Kazemi has been making a bunch of twitter bots that generate memes.
  • Vox Media has a meme generator called meme that’s open source.

Meme away!

The post Creating your own meme generator appeared first on CSS-Tricks.

More Unicode Patterns

Post pobrano z: More Unicode Patterns

Creating is the most intense excitement one can come to know.

Anni Albers, On Designing

I recently wrote a post — that was shared here on CSS-Tricks — where I looked at ways to use Unicode characters to create interesting (and random) patterns. Since then, I’ve continued to seek new characters to build new patterns. I even borrowed a book about Unicode from a local library.

(That’s a really thick book, by the way.)

It’s all up to your imagination to see the possible patterns a Unicode character can make. Although not all characters are good as patterns, the process is a good exercise for me.

And, aside from Unicode itself, the methods to build the patterns may not be so obvious. It usually takes a lot of inspiration and trial and error to come up with new ones.

More tiling

There are actually many ways to do tiling. Here’s one of my favorite tile patterns, which can be easily achieved using CSS grid:

A series of squares that vary in size from small to large and are arranged in a masonry pattern.
.grid {
  /* using `dense` to fill gaps automatically. */
  grid-auto-flow: dense;
}

.cell {
  /* using `span` to change cell size */
  grid-column-end: span <num>;
  grid-row-end: span <num>;
}

Grid Invaders by Miriam Suzanne is a good example of this technique.

Now, what I’m trying to do is put some Unicode characters into this grid. And most importantly, update the font-size value according to the span of its cell.

A series of red and orange Chinese Unicode characters arranged in the grid pattern of the previous image.
Pattern using characters \2f3c through \2f9f

I only tested with Chrome on Mac. Some of the examples may look awful on other browsers/platforms.

.cell {
  /* ... */
  --n: <random-span>;
  grid-column-end: span var(--n);
  grid-row-end: span var(--n);
}

.cell:after {
  /* ... */
  font-size: calc(var(--n) * 2vmin);
}

It’s a bit like the Tag Cloud effect, but with CSS. Lots of patterns can be made this way.

A series of orange and red \2686 and \2689 Unicode characters arranged in the same grid pattern as the other examples.
Pattern using characters \2686 through \2689
Unicode characters \21b0, \21b1, \21b2 and \21b4 arranged in the same grid pattern as the other examples. The effect is like a series of arrows pointed in different directions.
Pattern using charaters \21b0, \21b1, \21b2 and \21b4

The span of the columns and rows don’t always have to be the same value. We can make small modifications by changing how many rows each cell spans:

The grid layout with taller columns now that each cell spans more rows.
.cell {
  /* only change the row span */
  grid-row-end: span <num>;
}

Since the font-size property scales up/down in both directions (vertically and horizontally), the scaleY() in the transform property will be used instead.

Red and blue diamond-shaped Unicode characters squeezed into the taller, thinner columns of the grid layout.
Pattern using characters \25c6 through \25c8
:after {
  /* ... */
  transform: scaleY(calc(var(--span) * 1.4));
}

And here’s another one, made by rotating the inner container of the grid to some degree.

Red and blue triangles pointed diagonally in the grid layout.

The triangles also can be drawn with clip-path and will be more responsive, but it’s nice to do something in a different way.

More modifications to the layout:

The grid layout with skewed cells so that they form repeating parallelograms instead of rectangles.
.column-odd {
  transform: skewY(40deg);
}

.column-even {
  transform: skewY(-40deg);
}

Now follow these transformations for each column.

Plus sign Unicode characters in green, red, yellow and gray that follow the parallelogram pattern of the updated grid, forming a crochet-like effect.
Pattern using characters \1690 through \1694

Composition

Many Unicode pairs share some kind of shape with different angles. For example, parentheses, brackets, and arrows with different that go in different directions. We can use this concept to combine the shapes and generate repeatable patterns.

This pattern uses less-than and greater-than signs for the base:

Wavy pattern using <code><</code> and <code>></code>
:nth-child(odd):after {
  content: '<';
}

:nth-child(even):after {
  content: '>';
}

Here we go with parentheses:

A wavy pattern using ( and )
:nth-child(odd):after {
  content: '(';
}

:nth-child(even):after {
  content: ')';
}

These are characters we use everyday. However, they give us a fresh look and feeling when they are arranged in a new way.

There’s another pair of characters, , and . Placing them in the grid and scaling to a proper value connect them together into a seamless pattern:

It’s like weaving with characters! We can even take it up a notch by rotating things:

Pattern using \169b and \169c

Rings

Last week, I joined a CodePen Challenge that challenged the group to make a design out of the sub and sup elements. As I experimented with them, I noticed that the two tags scaled down automatically when nested.

So, I tried to put them around a circle:

.first-level {
  /* Slice the circle into many segments. */
  transform: rotate(
    calc(360deg / var(--slice) * var(--n))
  );
}

Suddenly, I realized this method can be used to generate background patterns, too. The results are pretty nice.

The Unicode characters for less-than and greater-than signs repeated in a circle that starts large around the edge and narrows in, like the characters are flushing down a drain.
Pattern using \003e
sub:after, sup:after {
  content: '\003e';
}

The interesting thing is that changing a single character can end up with very different results.

Adding the Unicode character \002e creates the same circular pattern, but with arrows and dots.
Combining \002e and \003e together to form a pattern
Combining \25c9 and \2234 creates a different effect in the same circular layout

Wrapping up

That’s all for now! The color palettes used in this article are from Color Hunt and Coolors.co.

The examples are generated with css-doodle, except for Ring examples in the last section. Everything here can be found in this CodePen collection.

Hope you like them and thanks for reading!

The post More Unicode Patterns appeared first on CSS-Tricks.