Javascript HTML 书籍式分页

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3636052/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 05:31:48  来源:igfitidea点击:

HTML book-like pagination

javascripthtmlcsswebkitpagination

提问by hpique

How can I split the content of a HTML file in screen-sized chunks to "paginate" it in a WebKit browser?

如何将 HTML 文件的内容拆分为屏幕大小的块以在 WebKit 浏览器中对其进行“分页”?

Each "page" should show a complete amount of text. This means that a line of text must not be cut in half in the top or bottom border of the screen.

每个“页面”应显示完整数量的文本。这意味着不得在屏幕的顶部或底部边框中将一行文本切成两半。

Edit

编辑

This question was originally tagged "Android" as my intent is to build an Android ePub reader. However, it appears that the solution can be implemented just with JavaScript and CSS so I broadened the scope of the question to make it platform-independent.

这个问题最初被标记为“Android”,因为我的目的是构建一个 Android ePub 阅读器。但是,该解决方案似乎可以仅使用 JavaScript 和 CSS 来实现,因此我扩大了问题的范围,使其与平台无关。

采纳答案by Engin Kurutepe

Building on Dan's answer here is my solution for this problem, with which I was struggling myself until just now. (this JS works on iOS Webkit, no guarantees for android, but please let me know the results)

建立在丹在这里的答案是我对这个问题的解决方案,直到现在我一直在努力解决这个问题。(此JS适用于iOS Webkit,不保证适用于Android,但请告诉我结果)

var desiredHeight;
var desiredWidth;
var bodyID = document.getElementsByTagName('body')[0];
totalHeight = bodyID.offsetHeight;
pageCount = Math.floor(totalHeight/desiredHeight) + 1;
bodyID.style.padding = 10; //(optional) prevents clipped letters around the edges
bodyID.style.width = desiredWidth * pageCount;
bodyID.style.height = desiredHeight;
bodyID.style.WebkitColumnCount = pageCount;

Hope this helps...

希望这可以帮助...

回答by Kevin Coppock

Speaking from experience, expect to put a lot of time into this, even for a barebones viewer. An ePub reader was actually first big project I took on when I started learning C#, but the ePub standard is definitely pretty complex.

从经验来看,即使对于准系统的观众,也希望投入大量时间。当我开始学习 C# 时,ePub 阅读器实际上是我承担的第一个大项目,但 ePub 标准绝对非常复杂。

You can find the latest version of the spec for ePub here: http://www.idpf.org/specs.htmwhich includes the OPS (Open Publication Structure), OPF (Open Packaging Format), and OCF (OEBPS Container Format).

您可以在此处找到 ePub 规范的最新版本:http: //www.idpf.org/specs.htm,其中包括 OPS(开放发布结构)、OPF(开放打包格式)和 OCF(OEBPS 容器格式) .

Also, if it helps you at all, here is a link to the C# source code of the project I started on:

另外,如果它对您有帮助,这里是我开始的项目的 C# 源代码的链接:

https://www.dropbox.com/sh/50kxcr29831t854/MDITIklW3I/ePub%20Test.zip

https://www.dropbox.com/sh/50kxcr29831t854/MDITIklW3I/ePub%20Test.zip

It's not fleshed out at all; I haven't played with this for months, but if I remember correctly, just stick an ePub in the debug directory, and when you run the program just type some part of the name (e.g. Under the Dome, just type "dome") and it will display the details of the book.

它根本没有充实;我已经几个月没玩了,但如果我没记错的话,只需在调试目录中粘贴一个 ePub,当您运行程序时,只需输入名称的一部分(例如,在穹顶下,只需输入“穹顶”)它会显示这本书的详细信息。

I had it working correctly for a few books, but any eBooks from Google Books broke it completely. They have a completely bizarre implementation of ePub (to me, at least) compared to books from other sources.

我让它在几本书中正常工作,但任何来自 Google Books 的电子书都完全破坏了它。与其他来源的书籍相比,他们对 ePub 的实现非常奇怪(至少对我而言)。

Anyway, hopefully some of the structural code in there might help you out!

无论如何,希望那里的一些结构代码可以帮助你!

回答by Nacho L.

I've had to code something like this too, and my (working) solution is this:

我也不得不编写这样的代码,我的(工作)解决方案是这样的:

You have to apply these lines to the webview...

您必须将这些行应用到 webview ...

    webView_.getSettings().setUseWideViewPort(true);
    webView_.getSettings().setLayoutAlgorithm(LayoutAlgorithm.NARROW_COLUMNS);

Also, you have to inject some javascript. I've had tons of problems with the differents scales of my activity and the content rendered in the webview, so my solution doesn't take any kind of value from "outside".

此外,您必须注入一些 javascript。我的活动的不同规模和 webview 中呈现的内容有很多问题,所以我的解决方案不会从“外部”获取任何价值。

    webView_.setWebViewClient(new WebViewClient(){

            public void onPageFinished(WebView view, String url) {
                injectJavascript();
            }

        });

[...]

[...]

    public void injectJavascript() {
        String js = "javascript:function initialize() { " +
                "var d = document.getElementsByTagName('body')[0];" +
                "var ourH = window.innerHeight; " +
                "var ourW = window.innerWidth; " + 
                "var fullH = d.offsetHeight; " +
                "var pageCount = Math.floor(fullH/ourH)+1;" +
                "var currentPage = 0; " +
                "var newW = pageCount*ourW; " +
                "d.style.height = ourH+'px';" +
                "d.style.width = newW+'px';" +
                "d.style.webkitColumnGap = '2px'; " +
                "d.style.margin = 0; " +
                "d.style.webkitColumnCount = pageCount;" +
                "}";
        webView_.loadUrl(js);
        webView_.loadUrl("javascript:initialize()");
    }

Enjoy :)

享受 :)

回答by Dan

I recently attempted something similar to this and added some CSS styling to change the layout to horizontal instead of vertical. This gave me the desired effect without having to modify the content of the Epub in any way.

我最近尝试了类似的东西,并添加了一些 CSS 样式来将布局更改为水平而不是垂直。这给了我想要的效果,而无需以任何方式修改 Epub 的内容。

This code shouldwork.

这段代码应该可以工作。

mWebView.setWebViewClient(new WebViewClient() {
    public void onPageFinished(WebView view, String url) {

        // Column Count is just the number of 'screens' of text. Add one for partial 'screens'
        int columnCount = Math.floor(view.getHeight() / view.getWidth())+1;

        // Must be expressed as a percentage. If not set then the WebView will not stretch to give the desired effect.
        int columnWidth = columnCount * 100;

        String js = "var d = document.getElementsByTagName('body')[0];" + 
            "d.style.WebkitColumnCount=" + columnCount + ";" + 
            "d.style.WebkitColumnWidth='" + columnWidth + "%';";
        mWebView.loadUrl("javascript:(function(){" + js + "})()");
    }
});

mWebView.loadUrl("file:///android_asset/chapter.xml");

So, basically you're injecting JavaScript to change the styling of the body element after the chapter has been loaded (very important). The only downfall to this approach is when you have images in the content the calculated column count goes askew. It shouldn't be too hard to fix though. My attempt was going to be injecting some JavaScript to add width and height attributes to all images in the DOM that don't have any.

所以,基本上你是在注入 JavaScript 来在章节加载后改变 body 元素的样式(非常重要)。这种方法的唯一缺点是当您的内容中有图像时,计算出的列数会出现偏差。不过修复起来应该不会太难。我的尝试是注入一些 JavaScript 来为 DOM 中没有任何图像的所有图像添加宽度和高度属性。

Hope it helps.

希望能帮助到你。

-Dan

-担

回答by LarsH

Maybe it would work to use XSL-FO. This seems heavy for a mobile device, and maybe it's overkill, but it should work, and you wouldn't have to implement the complexities of good pagination (e.g. how do you make sure that each screen doesn't cut text in half) yourself.

也许使用XSL-FO会起作用。这对于移动设备来说似乎很重,也许有点矫枉过正,但它应该可以工作,而且您不必自己实现良好分页的复杂性(例如,您如何确保每个屏幕不会将文本切成两半) .

The basic idea would be:

基本思想是:

  • transform the XHTML (and other EPUB stuff) to XSL-FO using XSLT.
  • use an XSL-FO processor to render the XSL-FO into a paged format that you can display on the mobile device, such as PDF (can you display that?)
  • 使用 XSLT 将 XHTML(和其他 EPUB 内容)转换为 XSL-FO。
  • 使用 XSL-FO 处理器将 XSL-FO 呈现为可以在移动设备上显示的分页格式,例如 PDF(您能显示吗?)

I don't know whether there is an XSL-FO processor available for Android. You could try Apache FOP. RenderX (XSL-FO processor) has the advantage of having a paged-HTML output option, but again I don't know if it could run on Android.

我不知道是否有适用于 Android 的 XSL-FO 处理器。你可以试试 Apache FOP。RenderX(XSL-FO 处理器)具有分页HTML 输出选项的优势,但我不知道它是否可以在Android 上运行。

回答by Gautam Chibde

I was able to improve Nacho'ssolution to get horizontal swipe paging effect with WebView. You can find solution hereand example code.

我能够改进Nacho 的解决方案,以通过 WebView 获得水平滑动分页效果。您可以在此处找到解决方案和示例代码

Edit:

编辑:

Solution code.

解决方案代码。

MainActivity.java

主活动.java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        WebView.setWebContentsDebuggingEnabled(true);
    }
    wv = (HorizontalWebView) findViewById(R.id.web_view);
    wv.getSettings().setJavaScriptEnabled(true);
    wv.setWebViewClient(new WebViewClient() {

        public void onPageFinished(WebView view, String url) {
            injectJavascript();
        }
    });

    wv.setWebChromeClient(new WebChromeClient() {
        @Override
        public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
            int pageCount = Integer.parseInt(message);
            wv.setPageCount(pageCount);
            result.confirm();
            return true;
        }
    });
    wv.loadUrl("file:///android_asset/ch03.html");   // now it will not fail here
}

private void injectJavascript() {
    String js = "function initialize(){\n" +
            "    var d = document.getElementsByTagName('body')[0];\n" +
            "    var ourH = window.innerHeight;\n" +
            "    var ourW = window.innerWidth;\n" +
            "    var fullH = d.offsetHeight;\n" +
            "    var pageCount = Math.floor(fullH/ourH)+1;\n" +
            "    var currentPage = 0;\n" +
            "    var newW = pageCount*ourW;\n" +
            "    d.style.height = ourH+'px';\n" +
            "    d.style.width = newW+'px';\n" +
            "    d.style.margin = 0;\n" +
            "    d.style.webkitColumnCount = pageCount;\n" +
            "    return pageCount;\n" +
            "}";
    wv.loadUrl("javascript:" + js);
    wv.loadUrl("javascript:alert(initialize())");
}

In my WebChromeClient's onJsAlert get the number of horizontal pages which i pass to the custom HorizontalWebView to implement paging effect

在我的 WebChromeClient 的 onJsAlert 中获取我传递给自定义 Horizo​​ntalWebView 以实现分页效果的水平页面数

HorizontalWebView.java

水平WebView.java

public class HorizontalWebView extends WebView {
    private float x1 = -1;
    private int pageCount = 0;

    public HorizontalWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                x1 = event.getX();
                break;
            case MotionEvent.ACTION_UP:
                float x2 = event.getX();
                float deltaX = x2 - x1;
                if (Math.abs(deltaX) > 100) {
                    // Left to Right swipe action
                    if (x2 > x1) {
                        turnPageLeft();
                        return true;
                    }

                    // Right to left swipe action
                    else {
                        turnPageRight();
                        return true;
                    }

                }
                break;
        }
        return true;
    }

    private int current_x = 0;

    private void turnPageLeft() {
        if (getCurrentPage() > 0) {
            int scrollX = getPrevPagePosition();
            loadAnimation(scrollX);
            current_x = scrollX;
            scrollTo(scrollX, 0);
        }
    }

    private int getPrevPagePosition() {
        int prevPage = getCurrentPage() - 1;
        return (int) Math.ceil(prevPage * this.getMeasuredWidth());
    }

    private void turnPageRight() {
        if (getCurrentPage() < pageCount - 1) {
            int scrollX = getNextPagePosition();
            loadAnimation(scrollX);
            current_x = scrollX;
            scrollTo(scrollX, 0);
        }
    }

    private void loadAnimation(int scrollX) {
        ObjectAnimator anim = ObjectAnimator.ofInt(this, "scrollX",
                current_x, scrollX);
        anim.setDuration(500);
        anim.setInterpolator(new LinearInterpolator());
        anim.start();
    }

    private int getNextPagePosition() {
        int nextPage = getCurrentPage() + 1;
        return (int) Math.ceil(nextPage * this.getMeasuredWidth());
    }

    public int getCurrentPage() {
        return (int) (Math.ceil((double) getScrollX() / this.getMeasuredWidth()));
    }

    public void setPageCount(int pageCount) {
        this.pageCount = pageCount;
    }
}

回答by Frank

There is several ways this could be done. If every line is in its own element all you have to do is to check if one of it's edges goes outside of the view (either the browsers, or the "book page").

有几种方法可以做到这一点。如果每一行都在它自己的元素中,你所要做的就是检查它的一个边缘是否在视图之外(浏览器或“书页”)。

If you want to know how many "pages" there is going to be in advance, just temporary move them into the view and get what line a page ends. This could potentially be slow because of that page reflow is needed for the browser to know where anything is.

如果您想提前知道将有多少“页面”,只需将它们临时移动到视图中并获取页面结束的行。这可能会很慢,因为浏览器需要页面重排才能知道任何东西在哪里。

Otherwise I think that you could use the HTML5 canvas element to measure text and / or draw text.

否则我认为您可以使用 HTML5 canvas 元素来测量文本和/或绘制文本。

Some info on that here: https://developer.mozilla.org/en/Drawing_text_using_a_canvashttp://uupaa-js-spinoff.googlecode.com/svn/trunk/uupaa-excanvas.js/demo/8_2_canvas_measureText.html

这里的一些信息:https: //developer.mozilla.org/en/Drawing_text_using_a_canvas http://uupaa-js-spinoff.googlecode.com/svn/trunk/uupaa-excanvas.js/demo/8_2_canvas_measureText.html

回答by Dennis

Had this same problem recently and inspired by the answers found a plain CSS solution using CSS3's column-* attributes:

最近遇到了同样的问题,并受到答案的启发,找到了一个使用 CSS3 的 column-* 属性的简单 CSS 解决方案:

/* CSS */
.chapter {
    width: 600px;
    padding: 60px 10px;
    -webkit-column-gap: 40px;
    -webkit-column-width: 150px;
    -webkit-column-count: 2;
    height:400px;
}

/* HTML */
<div class="chapter">
    your long lorem ipsum arbitrary HTML
</div>

The example above gives great results on a retina iPhone. Playing around with the different attributes yields in different spacing between the pages and such.

上面的例子在视网膜 iPhone 上给出了很好的结果。使用不同的属性会产生不同的页面间距等。

If you need to support multiple chapters for instance which need to start on new pages, there's an XCode 5 example on github: https://github.com/dmrschmidt/ios_uiwebview_pagination

如果您需要支持多个章节,例如需要从新页面开始,github 上有一个 XCode 5 示例:https: //github.com/dmrschmidt/ios_uiwebview_pagination

回答by kagali-san

You can look at http://www.litres.ru/static/OR/or.html?data=/static/trials/00/42/47/00424722.gur.html&art=424722&user=0&trial=1but the code may be heavily obfuscated, so just use Firebug to inspect DOM.

您可以查看http://www.litres.ru/static/OR/or.html?data=/static/trials/00/42/47/00424722.gur.html&art=424722&user=0&trial=1但代码可能被严重混淆,所以只需使用 Firebug 来检查 DOM。

If the link isn't working, comment - would give you fixed.

如果链接不起作用,请发表评论 - 会给你修复。

回答by Charles

You could split the pages in separate XHTML files and store them in a folder. Eg: page01, page02. You can then render those pages one by one underneath each other.

您可以将页面拆分为单独的 XHTML 文件并将它们存储在一个文件夹中。例如:page01、page02。然后,您可以将这些页面一个接一个地呈现在彼此下方。