Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Breakpoints implementation #70

Closed
neo773 opened this issue Jul 19, 2021 · 11 comments
Closed

Breakpoints implementation #70

neo773 opened this issue Jul 19, 2021 · 11 comments
Labels
enhancement New feature or request

Comments

@neo773
Copy link

neo773 commented Jul 19, 2021

Hi there, I was looking through the Documentation and couldn't find a breakpoint parameter that Swiper offers. The library is literally unusable on mobile without this parameter.

breakpoints: { 320: { slidesPerView: 1, spaceBetween: 20 }, 480: { slidesPerView: 1, spaceBetween: 30 }, 1112: { slidesPerView: 3, spaceBetween: 30 }, 1536: { slidesPerView: 3, spaceBetween: 0 } }

@pryley
Copy link

pryley commented Sep 10, 2021

Why not do something like this?

const breakpoints = {
  320: {
    slidesPerView: 1,
    spaceBetween: 20,
  },
  480: {
    slidesPerView: 1,
    spaceBetween: 30,
  },
  1112: {
    slidesPerView: 3,
    spaceBetween: 30,
  },
  1536: {
    slidesPerView: 3,
    spaceBetween: 0,
  }
}

const swiper = new Swiper('.swiper-container');

const swiperResize = () => {
  for (const [breakpoint, options] of Object.entries(breakpoints)) {
    if (swiper.env.element.$el.offsetWidth <= +breakpoint) {
      swiper.options = Object.assign(swiper.options, options)
      break
    }
  }
  swiper.updateSize()
}

const swiperResizeListener = _.debounce(swiperResize, 200)

window.addEventListener('resize', swiperResizeListener, { passive: true })
swiper.on('after-destroy', () => window.removeEventListener('resize', swiperResizeListener))

swiperResize()

@pryley
Copy link

pryley commented Sep 11, 2021

@joe223

What about creating an official plugin for breakpoints?

I started a basic implementation which includes a built-in rAF debounce: https://gist.github.com/pryley/3bc6d2f01aa5b68f829a5fbeb19abbd8

A few caveats:

  1. It does not take swiper direction into account (you may also wish to change instance.env.element.$el.offsetWidth to instance.env.measure.viewSize)
  2. You may need to convert to Typescript
  3. It assumes that the breakpoints object is in the format above.
  4. The debounce is set to 200ms, you may want this to be an option?
  5. There is no check for addEventListener { passive: true } support

Here is a demo: https://tiny-swiper2-demo-plugin-breakpoints.stackblitz.io

@joe223
Copy link
Owner

joe223 commented Sep 17, 2021

@pryley That's really great!Would you like to create a PR :) ?

Here are some questions I considered about

  1. Try using env.measure instead of element, avoid accessing real DOM data in state Layer. That's means you are exactly right, instance.env.measure.viewSize would be better!
  2. Definitely yes
  3. I think there is no problem if treat breakpoints‘s format as a Pact/Convention
  4. An option should be better
  5. It won't break the program if { passive: true } is not supported

Btw, would it be better if we provide throttle and debounce as common utils?

@pryley
Copy link

pryley commented Sep 17, 2021

@joe223

  1. The only reason I didn't use instance.env.measure.viewSize was because that value can represent either the width or height depending on the direction of the swiper.
  2. You would need to help with the Typescript conversion as I'm not that familiar with it

Regarding throttle and debounce, it could be useful to provide these as utils if they are only imported as needed. The debounce implementation was taken from the lodash function and stripped down as much as possible.

@joe223
Copy link
Owner

joe223 commented Sep 18, 2021

@pryley So, the breakpoints is only for horizontal direction right? 🤔

@pryley
Copy link

pryley commented Sep 20, 2021

Yes. The resize event is triggered by the window, but the breakpoint size is defined by the swiper width, not the window size.

How would you suggest this otherwise?

@joe223
Copy link
Owner

joe223 commented Sep 22, 2021

@pryley How about adding a new property of measure which means the another dimension of view size and cross with slide direction?

@pryley
Copy link

pryley commented Sep 23, 2021

I'm not sure height breakpoints would be useful. I think you would be more likely to use a breakpoints feature to change the direction of a swiper rather than to determine if a breakpoint is triggered by vertical or horizontal resizing. The reason measuring by width is more practical is because most devices whhich allow you to resize a browser window (i.e. desktop browsers) have landscape screens.

What if we simply mirrored the breakpoint functionality of swiperjs? It provides two options:

  • breakpoints: params object
  • breakpointsBase: string ("window" or "container")

Swiperjs only measures width on resize, not height (unless using ratios). The breakpoint keys can be either integer units (as pixel values) or ratio strings (i.e. "@0.75"), however I think to keep it minimal only integer units are really necessary.

https://swiperjs.com/swiper-api#param-breakpoints

I have extracted the debounce functionality into it's own utility function here: https://gist.github.com/pryley/c822c23ec542b6d7b6196de4707c3bdf

So using the Debounce utility function and adding a breakpointsBase option it would look probably something like this (untested):

import { Debounce } from '../core/render/timing'
import { Options } from '../core/options'
import { SwiperInstance, SwiperPlugin } from '../core/index'

export type SwiperPluginBreakpointsInstance = {}
export type SwiperPluginBreakpointsOptions = {}

/**
 * TinySwiper plugin for breakpoints.
 *
 * @param {SwiperInstance} instance
 * @param {Options}
 */
export default <SwiperPlugin>function SwiperPluginBreakpoints (
    instance: SwiperInstance & {
        breakpoints?: SwiperPluginBreakpointsInstance
    },
    options: Options & {
        breakpoints?: SwiperPluginBreakpointsOptions,
        breakpointsBase?: string,
    },
): void {
    const isEnabled = Boolean(options.breakpoints)
    const breakpoints: SwiperPluginBreakpointsInstance = {
        update (): void {
            for (const [breakpoint, values] of Object.entries(options.breakpoints)) {
                if ('window' === options.breakpointsBase) {
                    if (window.matchMedia(`(min-width: ${breakpoint}px)`).matches) {
                        instance.options = Object.assign(instance.options, values)
                    }
                } else if (+breakpoint <= instance.env.element.$el.offsetWidth) {
                    instance.options = Object.assign(instance.options, values)
                }
            }
            instance.updateSize()
        },
    }
    if (!isEnabled) return
    const resizeListener = () => Debounce(breakpoints.update)() // the default timeout is 200ms
    instance.on('after-init', () => {
        window.addEventListener('resize', resizeListener, { passive: true })
        requestAnimationFrame(breakpoints.update)
    })
    instance.on('before-destroy', () => {
        window.removeEventListener('resize', resizeListener)
    })
}

I think it may be overkill to provide a debounce timeout option, might be better to just provide a sane default. It would be preferable to use ResizeObserver instead of debounce, but this has reduced browser support.

I won't submit a pull request as I am not familiar enough with Typescript; you would need to perform the actual implementation.

@joe223 joe223 added the enhancement New feature or request label Sep 25, 2021
@joe223
Copy link
Owner

joe223 commented Sep 26, 2021

@pryley Great, I'll get this done in Oct

@ghost
Copy link

ghost commented Dec 24, 2021

@joe223 Any updates on this?

@joe223
Copy link
Owner

joe223 commented Jan 29, 2022

Hi @pryley @joshauh46 , sorry for the late reply. 2.2.0 was released. API: https://tiny-swiper.js.org/docs/api#breakpoints. Demo: https://tiny-swiper.js.org/docs/demo#breakpoints

@joe223 joe223 closed this as completed Jan 29, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants